Migrate from Bootstrap to Tailwind CSS with comprehensive UI improvements

This commit represents a complete migration from Bootstrap to Tailwind CSS with modern, professional UI design:

## Major Changes:
- **Tailwind CSS Integration**: Added django-tailwind package with Node.js 18.x support
- **UI Redesign**: Complete template migration to modern black/white theme
- **Responsive Design**: Mobile-first approach with improved navigation
- **Database Enhancement**: Added DATABASE_URL support for external services (Neon, Supabase, Railway, etc.)
- **Documentation**: Updated README.md and DEPLOYMENT.md with Tailwind and database guidance

## Technical Improvements:
- Hot-reloading during development with django-browser-reload
- Production-optimized CSS builds with purging and minification
- Professional card layouts and components
- Sticky footer implementation
- Mobile-responsive navigation with hamburger menu
- Modern alert/message styling

## Files Modified:
- All HTML templates converted to Tailwind utility classes
- Added theme app with Tailwind configuration
- Updated Docker configuration for Node.js support
- Enhanced settings for third-party database services
- Comprehensive documentation updates

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Django Template 2025-09-11 15:10:35 +05:30
parent 85f68a8df9
commit cde04c8dbe
25 changed files with 2631 additions and 351 deletions

View File

@ -4,11 +4,31 @@ DEBUG=True
ALLOWED_HOSTS=localhost,127.0.0.1,0.0.0.0 ALLOWED_HOSTS=localhost,127.0.0.1,0.0.0.0
# Database Configuration # Database Configuration
# Option 1: Individual variables (for Docker Compose/local development)
DB_NAME=django_db DB_NAME=django_db
DB_USER=django_user DB_USER=django_user
DB_PASSWORD=django_password DB_PASSWORD=django_password
DB_HOST=db DB_HOST=db
DB_PORT=5432 DB_PORT=5432
DB_SSLMODE=prefer
# Option 2: DATABASE_URL (for external services like Neon, Heroku, Railway, etc.)
# Uncomment and use ONE of the following examples:
# Neon (https://neon.tech)
# DATABASE_URL=postgresql://user:password@ep-xxx.us-east-1.aws.neon.tech/database?sslmode=require
# Supabase (https://supabase.com)
# DATABASE_URL=postgresql://postgres:password@db.xxx.supabase.co:5432/postgres?sslmode=require
# Railway (https://railway.app)
# DATABASE_URL=postgresql://postgres:password@containers-us-west-xxx.railway.app:5432/railway
# Heroku Postgres
# DATABASE_URL=postgres://user:password@host:5432/database
# Amazon RDS
# DATABASE_URL=postgresql://user:password@your-rds-instance.amazonaws.com:5432/database
# Email Configuration (for production) # Email Configuration (for production)
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend

View File

@ -71,6 +71,9 @@ Dokploy is a modern deployment platform that makes it easy to deploy application
python manage.py migrate python manage.py migrate
python manage.py create_groups python manage.py create_groups
python manage.py createsuperuser python manage.py createsuperuser
# Build Tailwind CSS for production
python manage.py tailwind build
python manage.py collectstatic --noinput python manage.py collectstatic --noinput
``` ```
@ -82,6 +85,44 @@ Dokploy is a modern deployment platform that makes it easy to deploy application
- Configure Google OAuth2 callback URL: `https://your-domain.com/accounts/google/login/callback/` - Configure Google OAuth2 callback URL: `https://your-domain.com/accounts/google/login/callback/`
- Configure Facebook OAuth2 callback URL: `https://your-domain.com/accounts/facebook/login/callback/` - Configure Facebook OAuth2 callback URL: `https://your-domain.com/accounts/facebook/login/callback/`
## 🗄️ External Database Services
Instead of setting up your own PostgreSQL instance, you can use managed database services:
### Neon (Recommended)
1. **Create account** at [neon.tech](https://neon.tech)
2. **Create a database** in your Neon dashboard
3. **Copy connection string** from the dashboard
4. **Set environment variable:**
```env
DATABASE_URL=postgresql://user:password@ep-xxx.us-east-1.aws.neon.tech/database?sslmode=require
```
### Supabase
1. **Create project** at [supabase.com](https://supabase.com)
2. **Go to Settings > Database**
3. **Use connection pooler URL for production:**
```env
DATABASE_URL=postgresql://postgres:password@db.xxx.supabase.co:6543/postgres?sslmode=require
```
### Railway
1. **Connect GitHub repo** to Railway
2. **Add PostgreSQL service**
3. **Railway sets DATABASE_URL automatically**
### Amazon RDS
1. **Create RDS PostgreSQL instance**
2. **Configure security groups** for your application
3. **Set DATABASE_URL with RDS endpoint:**
```env
DATABASE_URL=postgresql://user:password@your-rds.amazonaws.com:5432/database
```
### Environment Variables Reference ### Environment Variables Reference
| Variable | Description | Required | Default | | Variable | Description | Required | Default |
@ -89,11 +130,13 @@ Dokploy is a modern deployment platform that makes it easy to deploy application
| `SECRET_KEY` | Django secret key | Yes | - | | `SECRET_KEY` | Django secret key | Yes | - |
| `DEBUG` | Debug mode | Yes | False | | `DEBUG` | Debug mode | Yes | False |
| `ALLOWED_HOSTS` | Allowed hostnames | Yes | - | | `ALLOWED_HOSTS` | Allowed hostnames | Yes | - |
| `DB_NAME` | Database name | Yes | django_db | | `DATABASE_URL` | Full database URL | No* | - |
| `DB_USER` | Database user | Yes | django_user | | `DB_NAME` | Database name | No* | django_db |
| `DB_PASSWORD` | Database password | Yes | - | | `DB_USER` | Database user | No* | django_user |
| `DB_HOST` | Database host | Yes | postgres | | `DB_PASSWORD` | Database password | No* | - |
| `DB_PORT` | Database port | Yes | 5432 | | `DB_HOST` | Database host | No* | postgres |
| `DB_PORT` | Database port | No* | 5432 |
| `DB_SSLMODE` | SSL mode for database | No | prefer |
| `EMAIL_HOST` | SMTP host | No | localhost | | `EMAIL_HOST` | SMTP host | No | localhost |
| `EMAIL_PORT` | SMTP port | No | 587 | | `EMAIL_PORT` | SMTP port | No | 587 |
| `EMAIL_HOST_USER` | SMTP username | No | - | | `EMAIL_HOST_USER` | SMTP username | No | - |
@ -103,6 +146,8 @@ Dokploy is a modern deployment platform that makes it easy to deploy application
| `FACEBOOK_APP_ID` | Facebook app ID | No | - | | `FACEBOOK_APP_ID` | Facebook app ID | No | - |
| `FACEBOOK_APP_SECRET` | Facebook app secret | No | - | | `FACEBOOK_APP_SECRET` | Facebook app secret | No | - |
**Note:** Either `DATABASE_URL` OR the individual `DB_*` variables are required, not both.
## 🐳 Docker Deployment ## 🐳 Docker Deployment
### Production Docker Compose ### Production Docker Compose
@ -227,6 +272,64 @@ Consider adding:
docker-compose -f docker-compose.prod.yml exec web python manage.py migrate docker-compose -f docker-compose.prod.yml exec web python manage.py migrate
``` ```
4. **Rebuild Tailwind CSS:**
```bash
docker-compose -f docker-compose.prod.yml exec web python manage.py tailwind build
docker-compose -f docker-compose.prod.yml exec web python manage.py collectstatic --noinput
```
## 🎨 Tailwind CSS Production Considerations
### Building CSS for Production
The project uses `django-tailwind` which requires Node.js to build CSS files:
1. **Ensure Node.js is available in production:**
```dockerfile
# Dockerfile already includes Node.js installation
RUN curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y nodejs
```
2. **Build process in CI/CD:**
```bash
# Install dependencies
python manage.py tailwind install
# Build production CSS
python manage.py tailwind build
# Collect static files
python manage.py collectstatic --noinput
```
3. **CSS Optimization:**
- Production builds are automatically minified
- Unused CSS is purged based on template scanning
- CSS files are versioned for cache busting
### Environment Variables
Add these to your production environment:
```env
# Tailwind CSS
TAILWIND_APP_NAME=theme
NODE_ENV=production
```
### Static Files Structure
After deployment, verify this structure:
```
/app/static/
├── css/
│ └── dist/
│ └── styles.css # Built Tailwind CSS
├── js/
└── ...
```
### Backup Strategy ### Backup Strategy
1. **Database backup:** 1. **Database backup:**
@ -261,6 +364,18 @@ Consider adding:
- Check firewall/security group settings - Check firewall/security group settings
- Test email backend configuration - Test email backend configuration
5. **Tailwind CSS not loading:**
- Verify Node.js is installed: `node --version`
- Check if CSS was built: `ls -la static/css/dist/`
- Rebuild CSS: `python manage.py tailwind build`
- Ensure static files are collected: `python manage.py collectstatic`
6. **Styling looks broken:**
- Check browser developer tools for CSS loading errors
- Verify CSS file exists and is accessible
- Clear browser cache and hard reload
- Check for console errors
### Getting Help ### Getting Help
- Check the application logs - Check the application logs

View File

@ -9,6 +9,9 @@ RUN apt-get update \
build-essential \ build-essential \
libpq-dev \ libpq-dev \
gettext \ gettext \
curl \
&& curl -fsSL https://deb.nodesource.com/setup_18.x | bash - \
&& apt-get install -y nodejs \
&& rm -rf /var/lib/apt/lists/* && rm -rf /var/lib/apt/lists/*
WORKDIR /app WORKDIR /app

208
README.md
View File

@ -1,6 +1,6 @@
# Django Boilerplate # Django Template
A production-ready Django boilerplate with built-in authentication, social login, role-based permissions, and Docker support. A production-ready Django template with built-in authentication, social login, role-based permissions, modern UI with Tailwind CSS, and Docker support.
## ✨ Features ## ✨ Features
@ -28,10 +28,13 @@ A production-ready Django boilerplate with built-in authentication, social login
- Nginx reverse proxy - Nginx reverse proxy
- Gunicorn WSGI server - Gunicorn WSGI server
- 🎨 **Modern Frontend** - 🎨 **Modern Frontend with Tailwind CSS**
- Bootstrap 5 integration - Tailwind CSS v3.4+ integration with django-tailwind
- Responsive design - Hot-reloading during development
- Clean, professional UI - Responsive design with mobile-first approach
- Professional black/white theme
- Modern card layouts and components
- Optimized CSS builds for production
- ⚙️ **Environment Management** - ⚙️ **Environment Management**
- Separate settings for development/production - Separate settings for development/production
@ -121,10 +124,73 @@ docker-compose exec web python manage.py createsuperuser
docker-compose exec web python manage.py create_superuser --email admin@example.com --password admin123 docker-compose exec web python manage.py create_superuser --email admin@example.com --password admin123
``` ```
### 7. Access the Application ### 7. Start Tailwind Development Server
- **Web Application**: http://localhost:8000 ```bash
- **Django Admin**: http://localhost:8000/admin/ # In a separate terminal, start Tailwind's watch mode for hot reloading
docker-compose exec web python manage.py tailwind start
```
### 8. Access the Application
- **Web Application**: http://localhost:8001 (or http://localhost:8000)
- **Django Admin**: http://localhost:8001/admin/
## 🎨 Tailwind CSS Development
### Hot Reloading Setup
The project uses `django-tailwind` for seamless Tailwind CSS integration:
1. **Development Mode:**
```bash
# Start Tailwind watch mode (automatically rebuilds CSS on changes)
docker-compose exec web python manage.py tailwind start
```
2. **Building for Production:**
```bash
# Build minified CSS for production
docker-compose exec web python manage.py tailwind build
```
3. **Customizing Styles:**
- Edit templates with Tailwind utility classes
- Modify `theme/static_src/src/styles.css` for custom CSS
- Update `theme/static_src/tailwind.config.js` for configuration
### Theme Structure
```
theme/
├── static_src/ # Tailwind source files
│ ├── src/
│ │ └── styles.css # Main Tailwind CSS file
│ ├── tailwind.config.js # Tailwind configuration
│ ├── package.json # Node.js dependencies
│ └── node_modules/ # Node.js packages
├── static/
│ └── css/
│ └── dist/
│ └── styles.css # Generated CSS file
└── templates/ # Theme templates
```
### Adding Custom Components
Create reusable Tailwind components in your templates:
```html
<!-- Card component example -->
<div class="bg-white rounded-lg shadow-lg border border-gray-200">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-xl font-semibold text-gray-900">Card Title</h2>
</div>
<div class="px-6 py-6">
<!-- Card content -->
</div>
</div>
```
## 🔧 Development Setup ## 🔧 Development Setup
@ -159,6 +225,117 @@ docker-compose exec web python manage.py create_superuser --email admin@example.
python manage.py runserver python manage.py runserver
``` ```
## 🗄️ Third-Party Database Services
The application supports external PostgreSQL services like Neon, Supabase, Railway, and others. You can use either individual environment variables or a single DATABASE_URL.
### Supported Services
- **[Neon](https://neon.tech)** - Serverless PostgreSQL with branching
- **[Supabase](https://supabase.com)** - Open source Firebase alternative
- **[Railway](https://railway.app)** - Deploy from GitHub in seconds
- **[ElephantSQL](https://www.elephantsql.com)** - PostgreSQL as a Service
- **[Amazon RDS](https://aws.amazon.com/rds)** - AWS managed databases
- **[Google Cloud SQL](https://cloud.google.com/sql)** - Google Cloud databases
- **Any PostgreSQL-compatible service**
### Configuration Methods
#### Method 1: DATABASE_URL (Recommended)
Set a single environment variable:
```env
DATABASE_URL=postgresql://user:password@host:port/database?sslmode=require
```
**Neon Example:**
```env
DATABASE_URL=postgresql://neondb_owner:npg_ZO9W1DxrTwJu@ep-solitary-water-a1ucgv2p-pooler.ap-southeast-1.aws.neon.tech/neondb?sslmode=require&channel_binding=require
```
**Supabase Example:**
```env
DATABASE_URL=postgresql://postgres:your-password@db.xxx.supabase.co:5432/postgres?sslmode=require
```
#### Method 2: Individual Variables
For more control, use separate environment variables:
```env
DB_NAME=your_database_name
DB_USER=your_username
DB_PASSWORD=your_password
DB_HOST=your-host.amazonaws.com
DB_PORT=5432
DB_SSLMODE=require # For secure connections
```
### Quick Setup Examples
#### Neon Database
1. **Create a Neon project** at [neon.tech](https://neon.tech)
2. **Copy your connection string** from the Neon dashboard
3. **Add to your .env file:**
```env
DATABASE_URL=postgresql://user:pass@ep-xxx.us-east-1.aws.neon.tech/dbname?sslmode=require
```
#### Supabase Database
1. **Create a Supabase project** at [supabase.com](https://supabase.com)
2. **Go to Settings > Database**
3. **Copy the connection string and add to .env:**
```env
DATABASE_URL=postgresql://postgres:your-password@db.xxx.supabase.co:5432/postgres?sslmode=require
```
#### Railway Database
1. **Deploy to Railway** from GitHub
2. **Add PostgreSQL plugin**
3. **Railway automatically sets DATABASE_URL**
### SSL and Security
For production databases, always use SSL:
```env
# Enable SSL for external databases
DB_SSLMODE=require # or 'prefer' for flexible connections
DATABASE_URL=postgresql://user:pass@host/db?sslmode=require
```
### Migration Commands
After configuring your external database:
```bash
# Run migrations
python manage.py migrate
# Create user groups
python manage.py create_groups
# Create admin user
python manage.py createsuperuser
```
### Docker with External Database
When using external databases with Docker, update your docker-compose.yml:
```yaml
services:
web:
# ... other config
environment:
- DATABASE_URL=postgresql://user:pass@external-host/db?sslmode=require
# Remove the 'db' service when using external database
```
## 🌐 Social Authentication Setup ## 🌐 Social Authentication Setup
### Google OAuth2 ### Google OAuth2
@ -252,6 +429,13 @@ django-template/
│ └── core/ # Core application logic │ └── core/ # Core application logic
├── templates/ # HTML templates ├── templates/ # HTML templates
├── static/ # Static files (CSS, JS, images) ├── static/ # Static files (CSS, JS, images)
├── theme/ # Tailwind CSS theme
│ ├── static_src/ # Tailwind source files
│ │ ├── src/ # CSS source files
│ │ ├── tailwind.config.js
│ │ └── package.json # Node.js dependencies
│ ├── static/ # Generated CSS files
│ └── templates/ # Theme-specific templates
├── requirements/ # Python dependencies ├── requirements/ # Python dependencies
├── Dockerfile # Docker configuration ├── Dockerfile # Docker configuration
├── docker-compose.yml # Development Docker Compose ├── docker-compose.yml # Development Docker Compose
@ -301,6 +485,12 @@ python manage.py create_groups
# Create superuser with admin role # Create superuser with admin role
python manage.py create_superuser --email admin@example.com --password admin123 python manage.py create_superuser --email admin@example.com --password admin123
# Tailwind CSS commands
python manage.py tailwind install # Install Tailwind CSS dependencies
python manage.py tailwind start # Start development server with hot reload
python manage.py tailwind build # Build production CSS
python manage.py tailwind check # Check for Tailwind updates
# Standard Django commands # Standard Django commands
python manage.py migrate python manage.py migrate
python manage.py collectstatic python manage.py collectstatic

View File

@ -29,11 +29,14 @@ THIRD_PARTY_APPS = [
'allauth.socialaccount', 'allauth.socialaccount',
'allauth.socialaccount.providers.google', 'allauth.socialaccount.providers.google',
'whitenoise.runserver_nostatic', 'whitenoise.runserver_nostatic',
'tailwind',
'django_browser_reload',
] ]
LOCAL_APPS = [ LOCAL_APPS = [
'apps.accounts', 'apps.accounts',
'apps.core', 'apps.core',
'theme',
] ]
INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS
@ -70,6 +73,22 @@ TEMPLATES = [
WSGI_APPLICATION = 'django_project.wsgi.application' WSGI_APPLICATION = 'django_project.wsgi.application'
import dj_database_url
# Database configuration with support for DATABASE_URL
DATABASE_URL = config('DATABASE_URL', default=None)
if DATABASE_URL:
# Use DATABASE_URL if provided (common for external services like Neon, Heroku, etc.)
DATABASES = {
'default': dj_database_url.config(
default=DATABASE_URL,
conn_max_age=600,
conn_health_checks=True,
)
}
else:
# Use individual environment variables (for Docker Compose and local development)
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.postgresql', 'ENGINE': 'django.db.backends.postgresql',
@ -78,6 +97,9 @@ DATABASES = {
'PASSWORD': config('DB_PASSWORD', default='django_password'), 'PASSWORD': config('DB_PASSWORD', default='django_password'),
'HOST': config('DB_HOST', default='localhost'), 'HOST': config('DB_HOST', default='localhost'),
'PORT': config('DB_PORT', default='5432'), 'PORT': config('DB_PORT', default='5432'),
'OPTIONS': {
'sslmode': config('DB_SSLMODE', default='prefer'),
},
} }
} }
@ -157,3 +179,9 @@ SOCIALACCOUNT_PROVIDERS = {
} }
SOCIALACCOUNT_LOGIN_ON_GET = True SOCIALACCOUNT_LOGIN_ON_GET = True
# Tailwind CSS Configuration
TAILWIND_APP_NAME = 'theme'
# Production static files configuration
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'

View File

@ -7,6 +7,9 @@ DEBUG = True
ALLOWED_HOSTS = ['localhost', '127.0.0.1', '0.0.0.0'] ALLOWED_HOSTS = ['localhost', '127.0.0.1', '0.0.0.0']
# Add browser reload middleware for development
MIDDLEWARE.insert(0, 'django_browser_reload.middleware.BrowserReloadMiddleware')
DATABASES = { DATABASES = {
'default': { 'default': {
'ENGINE': 'django.db.backends.postgresql', 'ENGINE': 'django.db.backends.postgresql',

View File

@ -14,3 +14,4 @@ urlpatterns = [
if settings.DEBUG: if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += [path("__reload__/", include("django_browser_reload.urls"))]

View File

@ -5,3 +5,5 @@ psycopg2-binary==2.9.7
Pillow==10.0.1 Pillow==10.0.1
whitenoise==6.6.0 whitenoise==6.6.0
gunicorn==21.2.0 gunicorn==21.2.0
django-tailwind[reload]==3.8.0
dj-database-url==2.1.0

View File

@ -1,22 +1,22 @@
// Custom JavaScript for Django Boilerplate // Custom JavaScript for Application
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
// Auto-dismiss alerts after 5 seconds
setTimeout(function() {
const alerts = document.querySelectorAll('.alert');
alerts.forEach(function(alert) {
const bsAlert = new bootstrap.Alert(alert);
bsAlert.close();
});
}, 5000);
// Add smooth scrolling to anchor links // Add smooth scrolling to anchor links
document.querySelectorAll('a[href^="#"]').forEach(anchor => { document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) { anchor.addEventListener('click', function (e) {
e.preventDefault(); e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({ const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth' behavior: 'smooth'
}); });
}
}); });
}); });
}); });
// Mobile menu toggle function
function toggleMobileMenu() {
const mobileMenu = document.getElementById('mobileMenu');
mobileMenu.classList.toggle('hidden');
}

View File

@ -1,88 +1,100 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block title %}Dashboard - Django Boilerplate{% endblock %} {% block title %}Dashboard{% endblock %}
{% block content %} {% block content %}
<div class="row"> <div class="mb-8">
<div class="col-md-12"> <h1 class="text-3xl font-bold text-gray-900 mb-2">Welcome to Your Dashboard</h1>
<h1>Welcome to Your Dashboard</h1> <p class="text-lg text-gray-600">Hello, <strong class="text-gray-900">{{ user.full_name|default:user.email }}</strong>!</p>
<p class="lead">Hello, <strong>{{ user.full_name|default:user.email }}</strong>!</p>
</div>
</div> </div>
<div class="row mt-4"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div class="col-md-6"> <div class="bg-white rounded-lg shadow-lg border border-gray-200">
<div class="card"> <div class="px-6 py-4 border-b border-gray-200">
<div class="card-header"> <h2 class="text-xl font-semibold text-gray-900">Account Information</h2>
<h5>Account Information</h5>
</div> </div>
<div class="card-body"> <div class="px-6 py-6">
<p><strong>Email:</strong> {{ user.email }}</p> <div class="space-y-4">
<p><strong>Name:</strong> {{ user.full_name|default:"Not set" }}</p> <p class="text-gray-700"><strong class="text-gray-900">Email:</strong> {{ user.email }}</p>
<p><strong>Username:</strong> {{ user.username }}</p> <p class="text-gray-700"><strong class="text-gray-900">Name:</strong> {{ user.full_name|default:"Not set" }}</p>
<p><strong>Member since:</strong> {{ user.date_joined|date:"M d, Y" }}</p> <p class="text-gray-700"><strong class="text-gray-900">Username:</strong> {{ user.username }}</p>
<p><strong>Email verified:</strong> <p class="text-gray-700"><strong class="text-gray-900">Member since:</strong> {{ user.date_joined|date:"M d, Y" }}</p>
<p class="text-gray-700">
<strong class="text-gray-900">Email verified:</strong>
{% if user.is_verified %} {% if user.is_verified %}
<span class="badge bg-success">Yes</span> <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">Yes</span>
{% else %} {% else %}
<span class="badge bg-warning">No</span> <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800">No</span>
{% endif %} {% endif %}
</p> </p>
<a href="{% url 'accounts:profile_edit' %}" class="btn btn-primary">Edit Profile</a> </div>
<div class="mt-6">
<a href="{% url 'accounts:profile_edit' %}" class="bg-black text-white px-4 py-2 rounded-md hover:bg-gray-800 transition-colors duration-200 inline-flex items-center">
Edit Profile
</a>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="bg-white rounded-lg shadow-lg border border-gray-200">
<div class="card"> <div class="px-6 py-4 border-b border-gray-200">
<div class="card-header"> <h2 class="text-xl font-semibold text-gray-900">Your Roles</h2>
<h5>Your Roles</h5>
</div> </div>
<div class="card-body"> <div class="px-6 py-6">
<div class="mb-4">
{% if user_groups %} {% if user_groups %}
<div class="flex flex-wrap gap-2">
{% for group in user_groups %} {% for group in user_groups %}
<span class="badge bg-secondary me-1">{{ group.name|title }}</span> <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800">{{ group.name|title }}</span>
{% endfor %} {% endfor %}
</div>
{% else %} {% else %}
<p class="text-muted">No roles assigned</p> <p class="text-gray-500">No roles assigned</p>
{% endif %} {% endif %}
</div>
<hr> <hr class="my-4 border-gray-200">
<h6>Access Levels:</h6> <div>
<ul class="list-unstyled"> <h3 class="text-sm font-semibold text-gray-900 mb-3">Access Levels:</h3>
<li> <ul class="space-y-2">
<i class="text-success"></i> User Dashboard <li class="flex items-center text-sm">
<span class="text-green-500 mr-2"></span>
<span class="text-gray-700">User Dashboard</span>
</li> </li>
{% if user.groups.all %} {% if user.groups.all %}
{% for group in user.groups.all %} {% for group in user.groups.all %}
{% if group.name == 'staff' or group.name == 'admin' %} {% if group.name == 'staff' or group.name == 'admin' %}
<li> <li class="flex items-center text-sm">
<i class="text-success"></i> <span class="text-green-500 mr-2"></span>
<a href="{% url 'core:staff_dashboard' %}">Staff Dashboard</a> <a href="{% url 'core:staff_dashboard' %}" class="text-gray-700 hover:text-black transition-colors duration-200">Staff Dashboard</a>
</li> </li>
{% endif %} {% endif %}
{% if group.name == 'admin' %} {% if group.name == 'admin' %}
<li> <li class="flex items-center text-sm">
<i class="text-success"></i> <span class="text-green-500 mr-2"></span>
<a href="{% url 'core:admin_dashboard' %}">Admin Dashboard</a> <a href="{% url 'core:admin_dashboard' %}" class="text-gray-700 hover:text-black transition-colors duration-200">Admin Dashboard</a>
</li> </li>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% if not user.groups.filter|length %} {% if not user.groups.filter|length %}
<li> <li class="flex items-center text-sm">
<i class="text-muted"></i> Staff Dashboard <span class="text-gray-400 mr-2"></span>
<span class="text-gray-500">Staff Dashboard</span>
</li> </li>
<li> <li class="flex items-center text-sm">
<i class="text-muted"></i> Admin Dashboard <span class="text-gray-400 mr-2"></span>
<span class="text-gray-500">Admin Dashboard</span>
</li> </li>
{% endif %} {% endif %}
{% else %} {% else %}
<li> <li class="flex items-center text-sm">
<i class="text-muted"></i> Staff Dashboard <span class="text-gray-400 mr-2"></span>
<span class="text-gray-500">Staff Dashboard</span>
</li> </li>
<li> <li class="flex items-center text-sm">
<i class="text-muted"></i> Admin Dashboard <span class="text-gray-400 mr-2"></span>
<span class="text-gray-500">Admin Dashboard</span>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>

View File

@ -1,67 +1,90 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block title %}Profile - Django Boilerplate{% endblock %} {% block title %}Profile{% endblock %}
{% block content %} {% block content %}
<div class="row"> <div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<div class="col-md-8"> <div class="lg:col-span-2">
<div class="card"> <div class="bg-white rounded-lg shadow-lg border border-gray-200">
<div class="card-header d-flex justify-content-between align-items-center"> <div class="px-6 py-4 border-b border-gray-200 flex justify-between items-center">
<h5>Your Profile</h5> <h2 class="text-xl font-semibold text-gray-900">Your Profile</h2>
<a href="{% url 'accounts:profile_edit' %}" class="btn btn-primary btn-sm">Edit Profile</a> <a href="{% url 'accounts:profile_edit' %}" class="bg-black text-white px-3 py-1.5 text-sm rounded-md hover:bg-gray-800 transition-colors duration-200">
Edit Profile
</a>
</div> </div>
<div class="card-body"> <div class="px-6 py-6">
<div class="row"> <div class="flex flex-col md:flex-row gap-6">
<div class="col-md-4 text-center"> <div class="flex-shrink-0 text-center">
{% if user.profile.avatar %} {% if user.profile.avatar %}
<img src="{{ user.profile.avatar.url }}" class="img-thumbnail" width="150" height="150"> <img src="{{ user.profile.avatar.url }}" class="w-32 h-32 rounded-full mx-auto border border-gray-200 object-cover" alt="Profile Avatar">
{% else %} {% else %}
<div class="bg-secondary d-flex align-items-center justify-content-center" style="width: 150px; height: 150px;"> <div class="w-32 h-32 rounded-full bg-gray-100 flex items-center justify-content-center mx-auto border border-gray-200">
<i class="text-white fs-1">👤</i> <span class="text-4xl text-gray-500">👤</span>
</div> </div>
{% endif %} {% endif %}
</div> </div>
<div class="col-md-8"> <div class="flex-1">
<h4>{{ user.full_name|default:user.email }}</h4> <h3 class="text-2xl font-bold text-gray-900 mb-1">{{ user.full_name|default:user.email }}</h3>
<p class="text-muted">{{ user.email }}</p> <p class="text-gray-600 mb-4">{{ user.email }}</p>
<div class="space-y-3">
{% if user.profile.bio %} {% if user.profile.bio %}
<p><strong>Bio:</strong> {{ user.profile.bio }}</p> <div>
<span class="text-sm font-medium text-gray-900">Bio:</span>
<p class="text-gray-700 mt-1">{{ user.profile.bio }}</p>
</div>
{% endif %} {% endif %}
{% if user.profile.location %} {% if user.profile.location %}
<p><strong>Location:</strong> {{ user.profile.location }}</p> <p class="text-gray-700">
<span class="font-medium text-gray-900">Location:</span> {{ user.profile.location }}
</p>
{% endif %} {% endif %}
{% if user.profile.birth_date %} {% if user.profile.birth_date %}
<p><strong>Birth Date:</strong> {{ user.profile.birth_date }}</p> <p class="text-gray-700">
<span class="font-medium text-gray-900">Birth Date:</span> {{ user.profile.birth_date }}
</p>
{% endif %} {% endif %}
{% if user.profile.phone_number %} {% if user.profile.phone_number %}
<p><strong>Phone:</strong> {{ user.profile.phone_number }}</p> <p class="text-gray-700">
{% endif %} <span class="font-medium text-gray-900">Phone:</span> {{ user.profile.phone_number }}
<p><strong>Member since:</strong> {{ user.date_joined|date:"M d, Y" }}</p>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5>Account Status</h5>
</div>
<div class="card-body">
<p><strong>Email verified:</strong>
{% if user.is_verified %}
<span class="badge bg-success">Yes</span>
{% else %}
<span class="badge bg-warning">No</span>
{% endif %}
</p> </p>
<p><strong>Account type:</strong> {{ user.groups.first.name|default:"User"|title }}</p> {% endif %}
<p class="text-gray-700">
<span class="font-medium text-gray-900">Member since:</span> {{ user.date_joined|date:"M d, Y" }}
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="lg:col-span-1">
<div class="bg-white rounded-lg shadow-lg border border-gray-200">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-xl font-semibold text-gray-900">Account Status</h2>
</div>
<div class="px-6 py-6">
<div class="space-y-4">
<div>
<span class="text-sm font-medium text-gray-900">Email verified:</span>
<div class="mt-1">
{% if user.is_verified %}
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">Verified</span>
{% else %}
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-yellow-100 text-yellow-800">Unverified</span>
{% endif %}
</div>
</div>
<div>
<span class="text-sm font-medium text-gray-900">Account type:</span>
<p class="text-gray-700 mt-1">{{ user.groups.first.name|default:"User"|title }}</p>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,25 +1,77 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block title %}Edit Profile - Django Boilerplate{% endblock %} {% block title %}Edit Profile{% endblock %}
{% block content %} {% block content %}
<div class="row justify-content-center"> <div class="max-w-2xl mx-auto">
<div class="col-md-8"> <div class="bg-white rounded-lg shadow-lg border border-gray-200">
<div class="card"> <div class="px-6 py-4 border-b border-gray-200">
<div class="card-header"> <h2 class="text-xl font-semibold text-gray-900">Edit Your Profile</h2>
<h5>Edit Your Profile</h5>
</div> </div>
<div class="card-body"> <div class="px-6 py-6">
<form method="post" enctype="multipart/form-data"> <form method="post" enctype="multipart/form-data" class="space-y-6">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }}
<div class="d-flex justify-content-between"> {% for field in form %}
<a href="{% url 'accounts:profile' %}" class="btn btn-secondary">Cancel</a> <div>
<button type="submit" class="btn btn-primary">Save Changes</button> <label for="{{ field.id_for_label }}" class="block text-sm font-medium text-gray-900 mb-1">
{{ field.label }}
</label>
{% if field.field.widget.input_type == 'file' %}
<input type="file"
name="{{ field.name }}"
id="{{ field.id_for_label }}"
class="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-medium file:bg-gray-50 file:text-gray-700 hover:file:bg-gray-100"
{% if field.field.required %}required{% endif %}>
{% elif field.field.widget.input_type == 'textarea' %}
<textarea name="{{ field.name }}"
id="{{ field.id_for_label }}"
rows="4"
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-black focus:border-black"
{% if field.field.required %}required{% endif %}
placeholder="{{ field.field.widget.attrs.placeholder|default:'' }}">{{ field.value|default:'' }}</textarea>
{% elif field.field.widget.input_type == 'date' %}
<input type="date"
name="{{ field.name }}"
id="{{ field.id_for_label }}"
value="{{ field.value|default:'' }}"
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-black focus:border-black"
{% if field.field.required %}required{% endif %}>
{% else %}
<input type="{{ field.field.widget.input_type|default:'text' }}"
name="{{ field.name }}"
id="{{ field.id_for_label }}"
value="{{ field.value|default:'' }}"
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-black focus:border-black"
{% if field.field.required %}required{% endif %}
placeholder="{{ field.field.widget.attrs.placeholder|default:'' }}">
{% endif %}
{% if field.help_text %}
<p class="mt-1 text-sm text-gray-500">{{ field.help_text }}</p>
{% endif %}
{% if field.errors %}
{% for error in field.errors %}
<p class="mt-1 text-sm text-red-600">{{ error }}</p>
{% endfor %}
{% endif %}
</div>
{% endfor %}
<div class="flex justify-between pt-4">
<a href="{% url 'accounts:profile' %}"
class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 transition-colors duration-200">
Cancel
</a>
<button type="submit"
class="bg-black text-white px-6 py-2 rounded-md hover:bg-gray-800 transition-colors duration-200">
Save Changes
</button>
</div> </div>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
</div>
{% endblock %} {% endblock %}

View File

@ -1,67 +1,91 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block title %}Admin Dashboard - Django Boilerplate{% endblock %} {% block title %}Admin Dashboard{% endblock %}
{% block content %} {% block content %}
<div class="row"> <div class="mb-8">
<div class="col-md-12"> <div class="flex items-center mb-4">
<h1>🔐 Admin Dashboard</h1> <span class="text-4xl mr-3">🔐</span>
<p class="lead">Administrative control panel</p> <div>
</div> <h1 class="text-3xl font-bold text-gray-900">Admin Dashboard</h1>
</div> <p class="text-lg text-gray-600">Administrative control panel</p>
<div class="row mt-4">
<div class="col-md-4">
<div class="card">
<div class="card-body text-center">
<h5 class="card-title">User Management</h5>
<p class="card-text">Manage user accounts, roles, and permissions</p>
<a href="/admin/accounts/user/" class="btn btn-primary">Manage Users</a>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
<div class="card"> <div class="bg-white rounded-lg shadow-lg border border-gray-200 hover:shadow-xl transition-shadow duration-200">
<div class="card-body text-center"> <div class="px-6 py-8 text-center">
<h5 class="card-title">Group Management</h5> <h3 class="text-xl font-semibold text-gray-900 mb-3">User Management</h3>
<p class="card-text">Configure user groups and permissions</p> <p class="text-gray-600 mb-6">Manage user accounts, roles, and permissions</p>
<a href="/admin/auth/group/" class="btn btn-primary">Manage Groups</a> <a href="/admin/accounts/user/" class="bg-black text-white px-6 py-2 rounded-md hover:bg-gray-800 transition-colors duration-200 inline-block">
Manage Users
</a>
</div>
</div>
<div class="bg-white rounded-lg shadow-lg border border-gray-200 hover:shadow-xl transition-shadow duration-200">
<div class="px-6 py-8 text-center">
<h3 class="text-xl font-semibold text-gray-900 mb-3">Group Management</h3>
<p class="text-gray-600 mb-6">Configure user groups and permissions</p>
<a href="/admin/auth/group/" class="bg-black text-white px-6 py-2 rounded-md hover:bg-gray-800 transition-colors duration-200 inline-block">
Manage Groups
</a>
</div>
</div>
<div class="bg-white rounded-lg shadow-lg border border-gray-200 hover:shadow-xl transition-shadow duration-200">
<div class="px-6 py-8 text-center">
<h3 class="text-xl font-semibold text-gray-900 mb-3">Site Administration</h3>
<p class="text-gray-600 mb-6">Access full Django admin interface</p>
<a href="/admin/" class="bg-black text-white px-6 py-2 rounded-md hover:bg-gray-800 transition-colors duration-200 inline-block">
Django Admin
</a>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-4"> <div class="bg-white rounded-lg shadow-lg border border-gray-200">
<div class="card"> <div class="px-6 py-4 border-b border-gray-200">
<div class="card-body text-center"> <h2 class="text-xl font-semibold text-gray-900">System Information</h2>
<h5 class="card-title">Site Administration</h5> </div>
<p class="card-text">Access full Django admin interface</p> <div class="px-6 py-6">
<a href="/admin/" class="btn btn-primary">Django Admin</a> <div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-4">Current User</h3>
<div class="space-y-3">
<p class="text-gray-700">
<span class="font-medium text-gray-900">Name:</span> {{ user.full_name|default:user.email }}
</p>
<p class="text-gray-700">
<span class="font-medium text-gray-900">Role:</span> Administrator
</p>
<p class="text-gray-700">
<span class="font-medium text-gray-900">Last Login:</span> {{ user.last_login|date:"M d, Y H:i" }}
</p>
</div> </div>
</div> </div>
</div> <div>
</div> <h3 class="text-lg font-semibold text-gray-900 mb-4">Quick Stats</h3>
<div class="space-y-3">
<div class="row mt-4"> <p class="text-gray-700 flex justify-between">
<div class="col-md-12"> <span class="font-medium text-gray-900">Total Users:</span>
<div class="card"> <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
<div class="card-header"> {{ users_count|default:"N/A" }}
<h5>System Information</h5> </span>
</div> </p>
<div class="card-body"> <p class="text-gray-700 flex justify-between">
<div class="row"> <span class="font-medium text-gray-900">Active Sessions:</span>
<div class="col-md-6"> <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
<h6>Current User</h6> {{ active_sessions|default:"N/A" }}
<p><strong>Name:</strong> {{ user.full_name|default:user.email }}</p> </span>
<p><strong>Role:</strong> Administrator</p> </p>
<p><strong>Last Login:</strong> {{ user.last_login|date:"M d, Y H:i" }}</p> <p class="text-gray-700 flex justify-between">
</div> <span class="font-medium text-gray-900">System Status:</span>
<div class="col-md-6"> <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
<h6>Quick Stats</h6> Online
<p><strong>Total Users:</strong> <span class="badge bg-info">{{ users_count|default:"N/A" }}</span></p> </span>
<p><strong>Active Sessions:</strong> <span class="badge bg-success">{{ active_sessions|default:"N/A" }}</span></p> </p>
<p><strong>System Status:</strong> <span class="badge bg-success">Online</span></p>
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,80 +1,107 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en" class="h-full">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Application{% endblock %}</title> <title>{% block title %}Application{% endblock %}</title>
{% load static %} {% load static %}
{% load tailwind_tags %}
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"> {% tailwind_css %}
<link rel="stylesheet" href="{% static 'css/style.css' %}"> <link rel="stylesheet" href="{% static 'css/style.css' %}">
</head> </head>
<body> <body class="h-full flex flex-col">
<nav class="navbar navbar-expand-lg navbar-dark bg-dark"> <nav class="bg-black shadow-lg">
<div class="container"> <div class="max-w-7xl mx-auto px-4">
<a class="navbar-brand" href="{% url 'core:home' %}">App</a> <div class="flex justify-between h-16">
<div class="flex items-center">
<a class="text-white text-xl font-semibold" href="{% url 'core:home' %}">App</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"> <div class="hidden md:flex items-center ml-10 space-x-8">
<span class="navbar-toggler-icon"></span> <a class="text-gray-300 hover:text-white px-3 py-2 text-sm font-medium" href="{% url 'core:home' %}">Home</a>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link" href="{% url 'core:home' %}">Home</a>
</li>
{% if user.is_authenticated %} {% if user.is_authenticated %}
<li class="nav-item"> <a class="text-gray-300 hover:text-white px-3 py-2 text-sm font-medium" href="{% url 'accounts:dashboard' %}">Dashboard</a>
<a class="nav-link" href="{% url 'accounts:dashboard' %}">Dashboard</a>
</li>
{% if user.groups.all %} {% if user.groups.all %}
{% for group in user.groups.all %} {% for group in user.groups.all %}
{% if group.name == 'staff' or group.name == 'admin' %} {% if group.name == 'staff' or group.name == 'admin' %}
<li class="nav-item"> <a class="text-gray-300 hover:text-white px-3 py-2 text-sm font-medium" href="{% url 'core:staff_dashboard' %}">Staff</a>
<a class="nav-link" href="{% url 'core:staff_dashboard' %}">Staff</a>
</li>
{% endif %} {% endif %}
{% if group.name == 'admin' %} {% if group.name == 'admin' %}
<li class="nav-item"> <a class="text-gray-300 hover:text-white px-3 py-2 text-sm font-medium" href="{% url 'core:admin_dashboard' %}">Admin</a>
<a class="nav-link" href="{% url 'core:admin_dashboard' %}">Admin</a>
</li>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% endif %} {% endif %}
{% endif %} {% endif %}
</ul> </div>
</div>
<ul class="navbar-nav"> <div class="hidden md:flex items-center space-x-4">
{% if user.is_authenticated %} {% if user.is_authenticated %}
<li class="nav-item dropdown"> <div class="relative group">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown"> <button class="text-gray-300 hover:text-white px-3 py-2 text-sm font-medium">
{{ user.full_name|default:user.email }} {{ user.full_name|default:user.email }}
</a> </button>
<ul class="dropdown-menu"> <div class="absolute right-0 w-48 mt-2 py-2 bg-white rounded-lg shadow-xl opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-300">
<li><a class="dropdown-item" href="{% url 'accounts:profile' %}">Profile</a></li> <a class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" href="{% url 'accounts:profile' %}">Profile</a>
<li><hr class="dropdown-divider"></li> <hr class="my-1">
<li><a class="dropdown-item" href="{% url 'account_logout' %}">Logout</a></li> <a class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" href="{% url 'account_logout' %}">Logout</a>
</ul> </div>
</li> </div>
{% else %} {% else %}
<li class="nav-item"> <a class="text-gray-300 hover:text-white px-3 py-2 text-sm font-medium" href="{% url 'account_login' %}">Login</a>
<a class="nav-link" href="{% url 'account_login' %}">Login</a> <a class="bg-white text-black px-4 py-2 rounded-md text-sm font-medium hover:bg-gray-100" href="{% url 'account_signup' %}">Sign Up</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'account_signup' %}">Sign Up</a>
</li>
{% endif %} {% endif %}
</ul> </div>
<!-- Mobile menu button -->
<div class="md:hidden flex items-center">
<button class="text-gray-300 hover:text-white" onclick="toggleMobileMenu()">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</button>
</div>
</div>
<!-- Mobile menu -->
<div id="mobileMenu" class="md:hidden hidden">
<div class="px-2 pt-2 pb-3 space-y-1">
<a class="text-gray-300 hover:text-white block px-3 py-2 text-base font-medium" href="{% url 'core:home' %}">Home</a>
{% if user.is_authenticated %}
<a class="text-gray-300 hover:text-white block px-3 py-2 text-base font-medium" href="{% url 'accounts:dashboard' %}">Dashboard</a>
{% if user.groups.all %}
{% for group in user.groups.all %}
{% if group.name == 'staff' or group.name == 'admin' %}
<a class="text-gray-300 hover:text-white block px-3 py-2 text-base font-medium" href="{% url 'core:staff_dashboard' %}">Staff</a>
{% endif %}
{% if group.name == 'admin' %}
<a class="text-gray-300 hover:text-white block px-3 py-2 text-base font-medium" href="{% url 'core:admin_dashboard' %}">Admin</a>
{% endif %}
{% endfor %}
{% endif %}
<a class="text-gray-300 hover:text-white block px-3 py-2 text-base font-medium" href="{% url 'accounts:profile' %}">Profile</a>
<a class="text-gray-300 hover:text-white block px-3 py-2 text-base font-medium" href="{% url 'account_logout' %}">Logout</a>
{% else %}
<a class="text-gray-300 hover:text-white block px-3 py-2 text-base font-medium" href="{% url 'account_login' %}">Login</a>
<a class="text-gray-300 hover:text-white block px-3 py-2 text-base font-medium" href="{% url 'account_signup' %}">Sign Up</a>
{% endif %}
</div>
</div> </div>
</div> </div>
</nav> </nav>
<main class="container mt-4"> <main class="flex-1 max-w-7xl mx-auto px-4 py-8 w-full">
{% if messages %} {% if messages %}
{% for message in messages %} {% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert"> <div class="mb-4 p-4 rounded-lg {% if message.tags == 'error' %}bg-red-100 text-red-700 border border-red-200{% elif message.tags == 'success' %}bg-green-100 text-green-700 border border-green-200{% elif message.tags == 'warning' %}bg-yellow-100 text-yellow-700 border border-yellow-200{% else %}bg-blue-100 text-blue-700 border border-blue-200{% endif %}" role="alert">
{{ message }} <div class="flex justify-between items-center">
<button type="button" class="btn-close" data-bs-dismiss="alert"></button> <span>{{ message }}</span>
<button type="button" class="text-gray-400 hover:text-gray-600" onclick="this.parentElement.parentElement.remove()">
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"></path>
</svg>
</button>
</div>
</div> </div>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
@ -83,13 +110,12 @@
{% endblock %} {% endblock %}
</main> </main>
<footer class="bg-dark text-light text-center py-3 mt-5"> <footer class="bg-black text-white text-center py-6 mt-auto">
<div class="container"> <div class="max-w-7xl mx-auto px-4">
<p>&copy; 2024 Django Boilerplate. Built with Django & Bootstrap.</p> <p class="text-sm text-gray-400">&copy; 2024 Your Application. All rights reserved.</p>
</div> </div>
</footer> </footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script src="{% static 'js/main.js' %}"></script> <script src="{% static 'js/main.js' %}"></script>
</body> </body>
</html> </html>

View File

@ -3,67 +3,73 @@
{% block title %}Home{% endblock %} {% block title %}Home{% endblock %}
{% block content %} {% block content %}
<div class="row"> <div class="max-w-4xl mx-auto">
<div class="col-md-8 mx-auto"> <div class="bg-gray-50 rounded-lg p-8 mb-12 text-center">
<div class="jumbotron bg-light p-5 rounded"> <h1 class="text-5xl font-bold text-gray-900 mb-4">Welcome</h1>
<h1 class="display-4">Welcome</h1> <p class="text-xl text-gray-600 mb-6 max-w-2xl mx-auto">
<p class="lead">
A modern web application with secure authentication, A modern web application with secure authentication,
role-based permissions, and powerful features. role-based permissions, and powerful features.
</p> </p>
<hr class="my-4"> <hr class="border-gray-300 my-6 w-24 mx-auto">
{% if user.is_authenticated %} {% if user.is_authenticated %}
<p>Hello, <strong>{{ user.full_name|default:user.email }}</strong>!</p> <p class="text-gray-700 mb-6">Hello, <strong class="text-gray-900">{{ user.full_name|default:user.email }}</strong>!</p>
<a class="btn btn-primary btn-lg" href="{% url 'accounts:dashboard' %}" role="button"> <a href="{% url 'accounts:dashboard' %}"
class="bg-black text-white px-8 py-3 rounded-md hover:bg-gray-800 transition-colors duration-200 text-lg font-medium inline-block">
Go to Dashboard Go to Dashboard
</a> </a>
{% else %} {% else %}
<p>Get started by creating an account or logging in.</p> <p class="text-gray-700 mb-6">Get started by creating an account or logging in.</p>
<a class="btn btn-primary btn-lg me-3" href="{% url 'account_signup' %}" role="button"> <div class="space-x-4">
<a href="{% url 'account_signup' %}"
class="bg-black text-white px-8 py-3 rounded-md hover:bg-gray-800 transition-colors duration-200 text-lg font-medium inline-block">
Sign Up Sign Up
</a> </a>
<a class="btn btn-outline-primary btn-lg" href="{% url 'account_login' %}" role="button"> <a href="{% url 'account_login' %}"
class="border-2 border-gray-900 text-gray-900 px-8 py-3 rounded-md hover:bg-gray-900 hover:text-white transition-colors duration-200 text-lg font-medium inline-block">
Login Login
</a> </a>
</div>
{% endif %} {% endif %}
</div> </div>
</div> </div>
</div>
<div class="row mt-5"> <div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<div class="col-md-4"> <div class="bg-white rounded-lg shadow-lg border border-gray-200 h-full">
<div class="card h-100"> <div class="p-6">
<div class="card-body"> <div class="flex items-center mb-4">
<h5 class="card-title">🔐 Authentication</h5> <span class="text-2xl mr-3">🔐</span>
<p class="card-text"> <h3 class="text-xl font-semibold text-gray-900">Authentication</h3>
</div>
<p class="text-gray-600 leading-relaxed">
Complete authentication system with email verification, Complete authentication system with email verification,
password reset, and social login (Google & Facebook). password reset, and social login (Google & Facebook).
</p> </p>
</div> </div>
</div> </div>
<div class="bg-white rounded-lg shadow-lg border border-gray-200 h-full">
<div class="p-6">
<div class="flex items-center mb-4">
<span class="text-2xl mr-3">👥</span>
<h3 class="text-xl font-semibold text-gray-900">Role-Based Access</h3>
</div> </div>
<div class="col-md-4"> <p class="text-gray-600 leading-relaxed">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">👥 Role-Based Access</h5>
<p class="card-text">
Built-in user group system with admin, staff, and user roles Built-in user group system with admin, staff, and user roles
for granular permission control. for granular permission control.
</p> </p>
</div> </div>
</div> </div>
<div class="bg-white rounded-lg shadow-lg border border-gray-200 h-full">
<div class="p-6">
<div class="flex items-center mb-4">
<span class="text-2xl mr-3">🐳</span>
<h3 class="text-xl font-semibold text-gray-900">Docker Ready</h3>
</div> </div>
<div class="col-md-4"> <p class="text-gray-600 leading-relaxed">
<div class="card h-100">
<div class="card-body">
<h5 class="card-title">🐳 Docker Ready</h5>
<p class="card-text">
Fully containerized with Docker Compose, PostgreSQL database, Fully containerized with Docker Compose, PostgreSQL database,
and production-ready configuration. and production-ready configuration.
</p> </p>
</div> </div>
</div> </div>
</div> </div>
</div>
{% endblock %} {% endblock %}

View File

@ -1,59 +1,72 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block title %}Staff Dashboard - Django Boilerplate{% endblock %} {% block title %}Staff Dashboard{% endblock %}
{% block content %} {% block content %}
<div class="row"> <div class="mb-8">
<div class="col-md-12"> <div class="flex items-center mb-4">
<h1>👨‍💼 Staff Dashboard</h1> <span class="text-4xl mr-3">👨‍💼</span>
<p class="lead">Staff management panel</p> <div>
</div> <h1 class="text-3xl font-bold text-gray-900">Staff Dashboard</h1>
</div> <p class="text-lg text-gray-600">Staff management panel</p>
<div class="row mt-4">
<div class="col-md-6">
<div class="card">
<div class="card-body text-center">
<h5 class="card-title">User Support</h5>
<p class="card-text">Assist users with account and technical issues</p>
<a href="#" class="btn btn-primary">Support Center</a>
</div> </div>
</div> </div>
</div> </div>
<div class="col-md-6"> <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6">
<div class="card"> <div class="bg-white rounded-lg shadow-lg border border-gray-200 hover:shadow-xl transition-shadow duration-200">
<div class="card-body text-center"> <div class="px-6 py-8 text-center">
<h5 class="card-title">Content Management</h5> <h3 class="text-xl font-semibold text-gray-900 mb-3">User Support</h3>
<p class="card-text">Manage and moderate platform content</p> <p class="text-gray-600 mb-6">Assist users with account and technical issues</p>
<a href="#" class="btn btn-primary">Content Panel</a> <a href="#" class="bg-black text-white px-6 py-2 rounded-md hover:bg-gray-800 transition-colors duration-200 inline-block">
Support Center
</a>
</div> </div>
</div> </div>
<div class="bg-white rounded-lg shadow-lg border border-gray-200 hover:shadow-xl transition-shadow duration-200">
<div class="px-6 py-8 text-center">
<h3 class="text-xl font-semibold text-gray-900 mb-3">Content Management</h3>
<p class="text-gray-600 mb-6">Manage and moderate platform content</p>
<a href="#" class="bg-black text-white px-6 py-2 rounded-md hover:bg-gray-800 transition-colors duration-200 inline-block">
Content Panel
</a>
</div>
</div> </div>
</div> </div>
<div class="row mt-4"> <div class="bg-white rounded-lg shadow-lg border border-gray-200">
<div class="col-md-12"> <div class="px-6 py-4 border-b border-gray-200">
<div class="card"> <h2 class="text-xl font-semibold text-gray-900">Staff Information</h2>
<div class="card-header">
<h5>Staff Information</h5>
</div> </div>
<div class="card-body"> <div class="px-6 py-6">
<div class="row"> <div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<div class="col-md-6"> <div>
<h6>Current User</h6> <h3 class="text-lg font-semibold text-gray-900 mb-4">Current User</h3>
<p><strong>Name:</strong> {{ user.full_name|default:user.email }}</p> <div class="space-y-3">
<p><strong>Role:</strong> Staff Member</p> <p class="text-gray-700">
<p><strong>Department:</strong> {{ user.profile.department|default:"General" }}</p> <span class="font-medium text-gray-900">Name:</span> {{ user.full_name|default:user.email }}
</div> </p>
<div class="col-md-6"> <p class="text-gray-700">
<h6>Quick Actions</h6> <span class="font-medium text-gray-900">Role:</span> Staff Member
<div class="d-grid gap-2"> </p>
<button class="btn btn-outline-primary btn-sm">View Reports</button> <p class="text-gray-700">
<button class="btn btn-outline-secondary btn-sm">Export Data</button> <span class="font-medium text-gray-900">Department:</span> {{ user.profile.department|default:"General" }}
<button class="btn btn-outline-info btn-sm">Send Notifications</button> </p>
</div> </div>
</div> </div>
<div>
<h3 class="text-lg font-semibold text-gray-900 mb-4">Quick Actions</h3>
<div class="space-y-3">
<button class="w-full px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 transition-colors duration-200 text-left">
View Reports
</button>
<button class="w-full px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 transition-colors duration-200 text-left">
Export Data
</button>
<button class="w-full px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 transition-colors duration-200 text-left">
Send Notifications
</button>
</div> </div>
</div> </div>
</div> </div>

0
theme/__init__.py Normal file
View File

5
theme/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class ThemeConfig(AppConfig):
name = 'theme'

1
theme/static_src/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

1642
theme/static_src/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
{
"name": "theme",
"version": "3.8.0",
"description": "",
"scripts": {
"start": "npm run dev",
"build": "npm run build:clean && npm run build:tailwind",
"build:clean": "rimraf ../static/css/dist",
"build:tailwind": "cross-env NODE_ENV=production tailwindcss --postcss -i ./src/styles.css -o ../static/css/dist/styles.css --minify",
"dev": "cross-env NODE_ENV=development tailwindcss --postcss -i ./src/styles.css -o ../static/css/dist/styles.css -w",
"tailwindcss": "node ./node_modules/tailwindcss/lib/cli.js"
},
"keywords": [],
"author": "",
"license": "MIT",
"devDependencies": {
"@tailwindcss/aspect-ratio": "^0.4.2",
"@tailwindcss/forms": "^0.5.7",
"@tailwindcss/typography": "^0.5.10",
"cross-env": "^7.0.3",
"postcss": "^8.4.32",
"postcss-import": "^15.1.0",
"postcss-nested": "^6.0.1",
"postcss-simple-vars": "^7.0.1",
"rimraf": "^5.0.5",
"tailwindcss": "^3.4.0"
}
}

View File

@ -0,0 +1,7 @@
module.exports = {
plugins: {
"postcss-import": {},
"postcss-simple-vars": {},
"postcss-nested": {}
},
}

View File

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@ -0,0 +1,57 @@
/**
* This is a minimal config.
*
* If you need the full config, get it from here:
* https://unpkg.com/browse/tailwindcss@latest/stubs/defaultConfig.stub.js
*/
module.exports = {
content: [
/**
* HTML. Paths to Django template files that will contain Tailwind CSS classes.
*/
/* Templates within theme app (<tailwind_app_name>/templates), e.g. base.html. */
'../templates/**/*.html',
/*
* Main templates directory of the project (BASE_DIR/templates).
* Adjust the following line to match your project structure.
*/
'../../templates/**/*.html',
/*
* Templates in other django apps (BASE_DIR/<any_app_name>/templates).
* Adjust the following line to match your project structure.
*/
'../../**/templates/**/*.html',
/**
* JS: If you use Tailwind CSS in JavaScript, uncomment the following lines and make sure
* patterns match your project structure.
*/
/* JS 1: Ignore any JavaScript in node_modules folder. */
// '!../../**/node_modules',
/* JS 2: Process all JavaScript files in the project. */
// '../../**/*.js',
/**
* Python: If you use Tailwind CSS classes in Python, uncomment the following line
* and make sure the pattern below matches your project structure.
*/
// '../../**/*.py'
],
theme: {
extend: {},
},
plugins: [
/**
* '@tailwindcss/forms' is the forms plugin that provides a minimal styling
* for forms. If you don't like it or have own styling for forms,
* comment the line below to disable '@tailwindcss/forms'.
*/
require('@tailwindcss/forms'),
require('@tailwindcss/typography'),
require('@tailwindcss/aspect-ratio'),
],
}

19
theme/templates/base.html Normal file
View File

@ -0,0 +1,19 @@
{% load static tailwind_tags %}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Django Tailwind</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
{% tailwind_css %}
</head>
<body class="bg-gray-50 font-serif leading-normal tracking-normal">
<div class="container mx-auto">
<section class="flex items-center justify-center h-screen">
<h1 class="text-5xl">Django + Tailwind = ❤️</h1>
</section>
</div>
</body>
</html>