mirror of
https://github.com/thecyberlearn/modern-django-starter.git
synced 2026-08-18 10:12:56 +00:00
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:
parent
85f68a8df9
commit
cde04c8dbe
20
.env.example
20
.env.example
@ -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
|
||||||
|
|||||||
125
DEPLOYMENT.md
125
DEPLOYMENT.md
@ -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
|
||||||
|
|||||||
@ -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
208
README.md
@ -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
|
||||||
|
|||||||
@ -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'
|
||||||
@ -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',
|
||||||
|
|||||||
@ -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"))]
|
||||||
@ -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
|
||||||
@ -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');
|
||||||
|
}
|
||||||
@ -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>
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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 %}
|
||||||
@ -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>
|
||||||
|
|||||||
@ -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>© 2024 Django Boilerplate. Built with Django & Bootstrap.</p>
|
<p class="text-sm text-gray-400">© 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>
|
||||||
@ -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 %}
|
||||||
@ -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
0
theme/__init__.py
Normal file
5
theme/apps.py
Normal file
5
theme/apps.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class ThemeConfig(AppConfig):
|
||||||
|
name = 'theme'
|
||||||
1
theme/static_src/.gitignore
vendored
Normal file
1
theme/static_src/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
node_modules
|
||||||
1642
theme/static_src/package-lock.json
generated
Normal file
1642
theme/static_src/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
28
theme/static_src/package.json
Normal file
28
theme/static_src/package.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
7
theme/static_src/postcss.config.js
Normal file
7
theme/static_src/postcss.config.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
module.exports = {
|
||||||
|
plugins: {
|
||||||
|
"postcss-import": {},
|
||||||
|
"postcss-simple-vars": {},
|
||||||
|
"postcss-nested": {}
|
||||||
|
},
|
||||||
|
}
|
||||||
3
theme/static_src/src/styles.css
Normal file
3
theme/static_src/src/styles.css
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
57
theme/static_src/tailwind.config.js
Normal file
57
theme/static_src/tailwind.config.js
Normal 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
19
theme/templates/base.html
Normal 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>
|
||||||
Loading…
Reference in New Issue
Block a user