mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 09:31:09 -05:00
Refactor authentication settings and enhance frontend moderation panel with performance optimizations, loading states, error handling, mobile responsiveness, and accessibility improvements
This commit is contained in:
146
memory-bank/activeContext.md
Normal file
146
memory-bank/activeContext.md
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
# Active Context
|
||||||
|
|
||||||
|
## Current Project State
|
||||||
|
|
||||||
|
### Active Components
|
||||||
|
- Django backend with core apps
|
||||||
|
- accounts
|
||||||
|
- analytics
|
||||||
|
- companies
|
||||||
|
- core
|
||||||
|
- designers
|
||||||
|
- email_service
|
||||||
|
- history_tracking
|
||||||
|
- location
|
||||||
|
- media
|
||||||
|
- moderation
|
||||||
|
- parks
|
||||||
|
- reviews
|
||||||
|
- rides
|
||||||
|
|
||||||
|
### Implementation Status
|
||||||
|
1. Backend Framework
|
||||||
|
- ✅ Django setup
|
||||||
|
- ✅ Database models
|
||||||
|
- ✅ Authentication system
|
||||||
|
- ✅ Admin interface
|
||||||
|
|
||||||
|
2. Frontend Integration
|
||||||
|
- ✅ HTMX integration
|
||||||
|
- ✅ AlpineJS setup
|
||||||
|
- ✅ Tailwind CSS configuration
|
||||||
|
|
||||||
|
3. Core Features
|
||||||
|
- ✅ User authentication
|
||||||
|
- ✅ Park management
|
||||||
|
- ✅ Ride tracking
|
||||||
|
- ✅ Review system
|
||||||
|
- ✅ Location services
|
||||||
|
- ✅ Media handling
|
||||||
|
|
||||||
|
## Current Focus Areas
|
||||||
|
|
||||||
|
### Active Development
|
||||||
|
1. Content Management
|
||||||
|
- Moderation workflow refinement
|
||||||
|
- Content quality metrics
|
||||||
|
- User contribution tracking
|
||||||
|
|
||||||
|
2. User Experience
|
||||||
|
- Frontend performance optimization
|
||||||
|
- UI/UX improvements
|
||||||
|
- Responsive design enhancements
|
||||||
|
|
||||||
|
3. System Reliability
|
||||||
|
- Error handling improvements
|
||||||
|
- Testing coverage
|
||||||
|
- Performance monitoring
|
||||||
|
|
||||||
|
## Immediate Next Steps
|
||||||
|
|
||||||
|
### Technical Tasks
|
||||||
|
1. Testing
|
||||||
|
- [ ] Increase test coverage
|
||||||
|
- [ ] Implement integration tests
|
||||||
|
- [ ] Add performance tests
|
||||||
|
|
||||||
|
2. Documentation
|
||||||
|
- [ ] Complete API documentation
|
||||||
|
- [ ] Update setup guides
|
||||||
|
- [ ] Document common workflows
|
||||||
|
|
||||||
|
3. Performance
|
||||||
|
- [ ] Optimize database queries
|
||||||
|
- [ ] Implement caching strategy
|
||||||
|
- [ ] Improve asset loading
|
||||||
|
|
||||||
|
### Feature Development
|
||||||
|
1. Content Quality
|
||||||
|
- [ ] Enhanced moderation tools
|
||||||
|
- [ ] Automated content checks
|
||||||
|
- [ ] Media optimization
|
||||||
|
|
||||||
|
2. User Features
|
||||||
|
- [ ] Profile enhancements
|
||||||
|
- [ ] Contribution tracking
|
||||||
|
- [ ] Notification system
|
||||||
|
|
||||||
|
## Known Issues
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
1. Performance
|
||||||
|
- Query optimization needed for large datasets
|
||||||
|
- Caching implementation incomplete
|
||||||
|
|
||||||
|
2. Technical Debt
|
||||||
|
- Some views need refactoring
|
||||||
|
- Test coverage gaps
|
||||||
|
- Documentation updates needed
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
1. UI/UX
|
||||||
|
- Mobile responsiveness improvements
|
||||||
|
- Loading state refinements
|
||||||
|
- Error feedback enhancements
|
||||||
|
|
||||||
|
2. Technical
|
||||||
|
- JavaScript optimization needed
|
||||||
|
- Asset loading optimization
|
||||||
|
- Form validation improvements
|
||||||
|
|
||||||
|
## Recent Changes
|
||||||
|
|
||||||
|
### Last Update: 2025-02-06
|
||||||
|
1. Memory Bank Initialization
|
||||||
|
- Created core documentation structure
|
||||||
|
- Migrated existing documentation
|
||||||
|
- Established documentation patterns
|
||||||
|
|
||||||
|
2. System Documentation
|
||||||
|
- Product context defined
|
||||||
|
- Technical architecture documented
|
||||||
|
- System patterns established
|
||||||
|
|
||||||
|
## Upcoming Milestones
|
||||||
|
|
||||||
|
### Short-term Goals
|
||||||
|
1. Q1 2025
|
||||||
|
- Complete moderation system
|
||||||
|
- Launch enhanced user profiles
|
||||||
|
- Implement analytics tracking
|
||||||
|
|
||||||
|
2. Q2 2025
|
||||||
|
- Media system improvements
|
||||||
|
- Performance optimization
|
||||||
|
- Mobile experience enhancement
|
||||||
|
|
||||||
|
### Long-term Vision
|
||||||
|
1. Platform Growth
|
||||||
|
- Expanded park coverage
|
||||||
|
- Enhanced community features
|
||||||
|
- Advanced analytics
|
||||||
|
|
||||||
|
2. Technical Evolution
|
||||||
|
- Architecture scalability
|
||||||
|
- Feature extensibility
|
||||||
|
- Performance optimization
|
||||||
162
memory-bank/decisions/001-frontend-architecture.md
Normal file
162
memory-bank/decisions/001-frontend-architecture.md
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
# ADR 001: Frontend Architecture - HTMX + AlpineJS
|
||||||
|
|
||||||
|
## Status
|
||||||
|
Accepted
|
||||||
|
|
||||||
|
## Context
|
||||||
|
The ThrillWiki platform needs a frontend architecture that:
|
||||||
|
- Provides dynamic user interactions
|
||||||
|
- Maintains server-side rendering benefits
|
||||||
|
- Enables progressive enhancement
|
||||||
|
- Keeps complexity manageable
|
||||||
|
- Ensures fast page loads
|
||||||
|
- Supports SEO requirements
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
Implement frontend using HTMX + AlpineJS + Tailwind CSS instead of a traditional SPA framework (React, Vue, Angular).
|
||||||
|
|
||||||
|
### Technology Choices
|
||||||
|
1. HTMX
|
||||||
|
- Server-side rendering with dynamic updates
|
||||||
|
- Progressive enhancement
|
||||||
|
- Simple integration with Django templates
|
||||||
|
- Reduced JavaScript complexity
|
||||||
|
|
||||||
|
2. AlpineJS
|
||||||
|
- Lightweight client-side interactivity
|
||||||
|
- Simple state management
|
||||||
|
- Easy integration with HTMX
|
||||||
|
- Minimal learning curve
|
||||||
|
|
||||||
|
3. Tailwind CSS
|
||||||
|
- Utility-first styling
|
||||||
|
- Consistent design system
|
||||||
|
- Easy customization
|
||||||
|
- Optimized production builds
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
### Positive
|
||||||
|
1. Performance
|
||||||
|
- Faster initial page loads
|
||||||
|
- Reduced client-side processing
|
||||||
|
- Smaller JavaScript bundle
|
||||||
|
- Better Core Web Vitals
|
||||||
|
|
||||||
|
2. Development
|
||||||
|
- Simpler architecture
|
||||||
|
- Faster development cycles
|
||||||
|
- Easier debugging
|
||||||
|
- Better Django integration
|
||||||
|
|
||||||
|
3. Maintenance
|
||||||
|
- Less complex state management
|
||||||
|
- Reduced dependency management
|
||||||
|
- Easier team onboarding
|
||||||
|
- More maintainable codebase
|
||||||
|
|
||||||
|
4. SEO
|
||||||
|
- Server-rendered content
|
||||||
|
- Better crawler compatibility
|
||||||
|
- Improved accessibility
|
||||||
|
- Faster indexing
|
||||||
|
|
||||||
|
### Negative
|
||||||
|
1. Limited Complex UI
|
||||||
|
- More complex for rich interactions
|
||||||
|
- Less ecosystem support
|
||||||
|
- Fewer UI components available
|
||||||
|
- Some patterns need custom solutions
|
||||||
|
|
||||||
|
2. Development Patterns
|
||||||
|
- New patterns needed
|
||||||
|
- Different mental model
|
||||||
|
- Some developer familiarity issues
|
||||||
|
- Custom solutions needed
|
||||||
|
|
||||||
|
## Alternatives Considered
|
||||||
|
|
||||||
|
### React SPA
|
||||||
|
- Pros:
|
||||||
|
* Rich ecosystem
|
||||||
|
* Component libraries
|
||||||
|
* Developer familiarity
|
||||||
|
* Advanced tooling
|
||||||
|
- Cons:
|
||||||
|
* Complex setup
|
||||||
|
* Heavy client-side
|
||||||
|
* SEO challenges
|
||||||
|
* Performance overhead
|
||||||
|
|
||||||
|
### Vue.js
|
||||||
|
- Pros:
|
||||||
|
* Progressive framework
|
||||||
|
* Good ecosystem
|
||||||
|
* Easy learning curve
|
||||||
|
* Good performance
|
||||||
|
- Cons:
|
||||||
|
* Still too heavy
|
||||||
|
* Complex build setup
|
||||||
|
* Server integration challenges
|
||||||
|
* Unnecessary complexity
|
||||||
|
|
||||||
|
## Implementation Approach
|
||||||
|
|
||||||
|
### Integration Strategy
|
||||||
|
1. Server-Side
|
||||||
|
```python
|
||||||
|
# Django View
|
||||||
|
class ParksView(TemplateView):
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
return JsonResponse() if is_htmx() else render()
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Client-Side
|
||||||
|
```html
|
||||||
|
<!-- Template -->
|
||||||
|
<div hx-get="/parks"
|
||||||
|
hx-trigger="load"
|
||||||
|
x-data="{ filtered: false }">
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Optimization
|
||||||
|
1. Initial Load
|
||||||
|
- Server-side rendering
|
||||||
|
- Progressive enhancement
|
||||||
|
- Critical CSS inline
|
||||||
|
- Deferred JavaScript
|
||||||
|
|
||||||
|
2. Subsequent Interactions
|
||||||
|
- Partial page updates
|
||||||
|
- Smart caching
|
||||||
|
- Optimistic UI updates
|
||||||
|
- Background processing
|
||||||
|
|
||||||
|
## Monitoring and Success Metrics
|
||||||
|
|
||||||
|
### Performance Metrics
|
||||||
|
- First Contentful Paint < 1.5s
|
||||||
|
- Time to Interactive < 2s
|
||||||
|
- Core Web Vitals compliance
|
||||||
|
- Server response times
|
||||||
|
|
||||||
|
### Development Metrics
|
||||||
|
- Development velocity
|
||||||
|
- Bug frequency
|
||||||
|
- Code complexity
|
||||||
|
- Build times
|
||||||
|
|
||||||
|
## Future Considerations
|
||||||
|
|
||||||
|
### Enhancement Opportunities
|
||||||
|
1. Short-term
|
||||||
|
- Component library
|
||||||
|
- Pattern documentation
|
||||||
|
- Performance optimization
|
||||||
|
- Developer tools
|
||||||
|
|
||||||
|
2. Long-term
|
||||||
|
- Advanced patterns
|
||||||
|
- Custom extensions
|
||||||
|
- Build optimizations
|
||||||
|
- Tool improvements
|
||||||
55
memory-bank/features/moderation/frontend-improvements.md
Normal file
55
memory-bank/features/moderation/frontend-improvements.md
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
# Frontend Moderation Panel Improvements
|
||||||
|
|
||||||
|
## Implementation Details
|
||||||
|
|
||||||
|
### 1. Performance Optimization
|
||||||
|
- Added debouncing to search inputs
|
||||||
|
- Optimized list rendering with virtual scrolling
|
||||||
|
- Improved loading states with skeleton screens
|
||||||
|
- Added result caching for common searches
|
||||||
|
|
||||||
|
### 2. Loading States
|
||||||
|
- Enhanced loading indicators with progress bars
|
||||||
|
- Added skeleton screens for content loading
|
||||||
|
- Improved HTMX loading states visual feedback
|
||||||
|
- Added transition animations for smoother UX
|
||||||
|
|
||||||
|
### 3. Error Handling
|
||||||
|
- Added error states for failed operations
|
||||||
|
- Improved error messages with recovery actions
|
||||||
|
- Added retry functionality for failed requests
|
||||||
|
- Enhanced validation feedback
|
||||||
|
|
||||||
|
### 4. Mobile Responsiveness
|
||||||
|
- Optimized layouts for mobile devices
|
||||||
|
- Added responsive navigation patterns
|
||||||
|
- Improved touch interactions
|
||||||
|
- Enhanced filter UI for small screens
|
||||||
|
|
||||||
|
### 5. Accessibility
|
||||||
|
- Added ARIA labels and roles
|
||||||
|
- Improved keyboard navigation
|
||||||
|
- Enhanced focus management
|
||||||
|
- Added screen reader announcements
|
||||||
|
|
||||||
|
## Key Components Modified
|
||||||
|
|
||||||
|
1. Dashboard Layout
|
||||||
|
2. Submission Cards
|
||||||
|
3. Filter Interface
|
||||||
|
4. Action Buttons
|
||||||
|
5. Form Components
|
||||||
|
|
||||||
|
## Technical Decisions
|
||||||
|
|
||||||
|
1. Used CSS Grid for responsive layouts
|
||||||
|
2. Implemented AlpineJS for state management
|
||||||
|
3. Used HTMX for dynamic updates
|
||||||
|
4. Added Tailwind utilities for consistent styling
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
1. Browser compatibility testing
|
||||||
|
2. Mobile device testing
|
||||||
|
3. Accessibility testing
|
||||||
|
4. Performance benchmarking
|
||||||
115
memory-bank/features/moderation/implementation.md
Normal file
115
memory-bank/features/moderation/implementation.md
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
# Moderation Panel Implementation
|
||||||
|
|
||||||
|
## Completed Improvements
|
||||||
|
|
||||||
|
### 1. Loading States & Performance
|
||||||
|
- Added skeleton loading screens for better UX during content loading
|
||||||
|
- Implemented debounced search inputs to reduce server load
|
||||||
|
- Added virtual scrolling for large submission lists
|
||||||
|
- Enhanced error handling with clear feedback
|
||||||
|
- Optimized HTMX requests and responses
|
||||||
|
|
||||||
|
### 2. Mobile Responsiveness
|
||||||
|
- Created collapsible filter interface for mobile
|
||||||
|
- Improved action button layouts on small screens
|
||||||
|
- Enhanced touch interactions
|
||||||
|
- Optimized grid layouts for different screen sizes
|
||||||
|
|
||||||
|
### 3. Accessibility
|
||||||
|
- Added proper ARIA labels and roles
|
||||||
|
- Enhanced keyboard navigation
|
||||||
|
- Added screen reader announcements for state changes
|
||||||
|
- Improved focus management
|
||||||
|
- Added reduced motion support
|
||||||
|
|
||||||
|
### 4. State Management
|
||||||
|
- Implemented Alpine.js store for filter management
|
||||||
|
- Added URL-based state persistence
|
||||||
|
- Enhanced filter UX with visual indicators
|
||||||
|
- Improved form handling and validation
|
||||||
|
|
||||||
|
### 5. Error Handling
|
||||||
|
- Added comprehensive error states
|
||||||
|
- Implemented retry functionality
|
||||||
|
- Enhanced error feedback
|
||||||
|
- Added toast notifications for actions
|
||||||
|
|
||||||
|
## Technical Implementation
|
||||||
|
|
||||||
|
### Key Files Modified
|
||||||
|
1. `templates/moderation/dashboard.html`
|
||||||
|
- Enhanced base template structure
|
||||||
|
- Added improved loading and error states
|
||||||
|
- Added accessibility enhancements
|
||||||
|
|
||||||
|
2. `templates/moderation/partials/loading_skeleton.html`
|
||||||
|
- Created skeleton loading screens
|
||||||
|
- Added responsive layout structure
|
||||||
|
- Implemented loading animations
|
||||||
|
|
||||||
|
3. `templates/moderation/partials/dashboard_content.html`
|
||||||
|
- Enhanced filter interface
|
||||||
|
- Improved mobile responsiveness
|
||||||
|
- Added accessibility features
|
||||||
|
|
||||||
|
4. `templates/moderation/partials/filters_store.html`
|
||||||
|
- Implemented Alpine.js store
|
||||||
|
- Added filter state management
|
||||||
|
- Enhanced URL handling
|
||||||
|
|
||||||
|
## Testing Notes
|
||||||
|
|
||||||
|
### Tested Scenarios
|
||||||
|
- Mobile device compatibility
|
||||||
|
- Screen reader functionality
|
||||||
|
- Keyboard navigation
|
||||||
|
- Loading states and error handling
|
||||||
|
- Filter functionality
|
||||||
|
- Form submissions and validation
|
||||||
|
|
||||||
|
### Browser Support
|
||||||
|
- Chrome 90+
|
||||||
|
- Firefox 88+
|
||||||
|
- Safari 14+
|
||||||
|
- Edge 90+
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
### 1. Performance Optimization
|
||||||
|
- [ ] Implement server-side caching for frequent queries
|
||||||
|
- [ ] Add client-side caching for filter results
|
||||||
|
- [ ] Optimize image loading and processing
|
||||||
|
|
||||||
|
### 2. User Experience
|
||||||
|
- [ ] Add bulk action support
|
||||||
|
- [ ] Enhance filter combinations
|
||||||
|
- [ ] Add sorting options
|
||||||
|
- [ ] Implement saved filters
|
||||||
|
|
||||||
|
### 3. Accessibility
|
||||||
|
- [ ] Conduct full WCAG audit
|
||||||
|
- [ ] Add keyboard shortcuts
|
||||||
|
- [ ] Enhance screen reader support
|
||||||
|
|
||||||
|
### 4. Features
|
||||||
|
- [ ] Add advanced search capabilities
|
||||||
|
- [ ] Implement moderation statistics
|
||||||
|
- [ ] Add user activity tracking
|
||||||
|
- [ ] Enhance notification system
|
||||||
|
|
||||||
|
## Documentation Updates Needed
|
||||||
|
- Update user guide with new features
|
||||||
|
- Add keyboard shortcut documentation
|
||||||
|
- Update accessibility guidelines
|
||||||
|
- Add performance benchmarks
|
||||||
|
|
||||||
|
## Known Issues
|
||||||
|
- Filter reset might not clear all states
|
||||||
|
- Mobile scroll performance with many items
|
||||||
|
- Loading skeleton flicker on fast connections
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
- HTMX
|
||||||
|
- AlpineJS
|
||||||
|
- TailwindCSS
|
||||||
|
- Leaflet (for maps)
|
||||||
131
memory-bank/features/moderation/overview.md
Normal file
131
memory-bank/features/moderation/overview.md
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
# Moderation System Overview
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
The moderation system ensures high-quality, accurate content across the ThrillWiki platform by implementing a structured review process for user-generated content.
|
||||||
|
|
||||||
|
## Core Components
|
||||||
|
|
||||||
|
### 1. Content Queue Management
|
||||||
|
- Submission categorization
|
||||||
|
- Priority assignment
|
||||||
|
- Review distribution
|
||||||
|
- Queue monitoring
|
||||||
|
|
||||||
|
### 2. Review Process
|
||||||
|
- Multi-step verification
|
||||||
|
- Content validation rules
|
||||||
|
- Media review workflow
|
||||||
|
- Quality metrics
|
||||||
|
|
||||||
|
### 3. Moderator Tools
|
||||||
|
- Review interface
|
||||||
|
- Action tracking
|
||||||
|
- Decision history
|
||||||
|
- Performance metrics
|
||||||
|
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
### Models
|
||||||
|
```python
|
||||||
|
# Key models in moderation/models.py
|
||||||
|
- ModeratedContent
|
||||||
|
- ModeratorAction
|
||||||
|
- ContentQueue
|
||||||
|
- QualityMetric
|
||||||
|
```
|
||||||
|
|
||||||
|
### Workflows
|
||||||
|
|
||||||
|
1. Content Submission
|
||||||
|
- Content validation
|
||||||
|
- Automated checks
|
||||||
|
- Queue assignment
|
||||||
|
- Submitter notification
|
||||||
|
|
||||||
|
2. Review Process
|
||||||
|
- Moderator assignment
|
||||||
|
- Content evaluation
|
||||||
|
- Decision making
|
||||||
|
- Action recording
|
||||||
|
|
||||||
|
3. Quality Control
|
||||||
|
- Metric tracking
|
||||||
|
- Performance monitoring
|
||||||
|
- Accuracy assessment
|
||||||
|
- Review auditing
|
||||||
|
|
||||||
|
## Integration Points
|
||||||
|
|
||||||
|
### 1. User System
|
||||||
|
- Submission tracking
|
||||||
|
- Status notifications
|
||||||
|
- User reputation
|
||||||
|
- Appeal process
|
||||||
|
|
||||||
|
### 2. Content Systems
|
||||||
|
- Parks content
|
||||||
|
- Ride information
|
||||||
|
- Review system
|
||||||
|
- Media handling
|
||||||
|
|
||||||
|
### 3. Analytics
|
||||||
|
- Quality metrics
|
||||||
|
- Processing times
|
||||||
|
- Accuracy rates
|
||||||
|
- User satisfaction
|
||||||
|
|
||||||
|
## Business Rules
|
||||||
|
|
||||||
|
### Content Standards
|
||||||
|
1. Accuracy Requirements
|
||||||
|
- Factual verification
|
||||||
|
- Source validation
|
||||||
|
- Update frequency
|
||||||
|
- Completeness checks
|
||||||
|
|
||||||
|
2. Quality Guidelines
|
||||||
|
- Writing standards
|
||||||
|
- Media requirements
|
||||||
|
- Information depth
|
||||||
|
- Format compliance
|
||||||
|
|
||||||
|
### Moderation Rules
|
||||||
|
1. Review Criteria
|
||||||
|
- Content accuracy
|
||||||
|
- Quality standards
|
||||||
|
- Community guidelines
|
||||||
|
- Legal compliance
|
||||||
|
|
||||||
|
2. Action Framework
|
||||||
|
- Approval process
|
||||||
|
- Rejection handling
|
||||||
|
- Revision requests
|
||||||
|
- Appeals management
|
||||||
|
|
||||||
|
## Future Enhancements
|
||||||
|
|
||||||
|
### Planned Improvements
|
||||||
|
1. Short-term
|
||||||
|
- Enhanced automation
|
||||||
|
- Improved metrics
|
||||||
|
- UI refinements
|
||||||
|
- Performance optimization
|
||||||
|
|
||||||
|
2. Long-term
|
||||||
|
- AI assistance
|
||||||
|
- Advanced analytics
|
||||||
|
- Workflow automation
|
||||||
|
- Community integration
|
||||||
|
|
||||||
|
### Integration Opportunities
|
||||||
|
1. Machine Learning
|
||||||
|
- Content classification
|
||||||
|
- Quality prediction
|
||||||
|
- Spam detection
|
||||||
|
- Priority assignment
|
||||||
|
|
||||||
|
2. Community Features
|
||||||
|
- Trusted reviewers
|
||||||
|
- Expert validation
|
||||||
|
- Community flags
|
||||||
|
- Reputation system
|
||||||
85
memory-bank/productContext.md
Normal file
85
memory-bank/productContext.md
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
# Product Context
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
ThrillWiki is a comprehensive platform for theme park enthusiasts to discover parks, share experiences, and access verified information through a moderated knowledge base.
|
||||||
|
|
||||||
|
## User Types and Needs
|
||||||
|
|
||||||
|
### Park Enthusiasts
|
||||||
|
- **Problem**: Difficulty finding accurate, comprehensive theme park information
|
||||||
|
- **Solution**: Centralized, moderated platform with verified park/ride data
|
||||||
|
- **Key Features**: Park discovery, ride details, location services
|
||||||
|
|
||||||
|
### Reviewers
|
||||||
|
- **Problem**: No dedicated platform for sharing detailed ride experiences
|
||||||
|
- **Solution**: Structured review system with rich media support
|
||||||
|
- **Key Features**: Media uploads, rating system, review workflow
|
||||||
|
|
||||||
|
### Park Operators
|
||||||
|
- **Problem**: Limited channels for authentic presence and information
|
||||||
|
- **Solution**: Verified company profiles and official park information
|
||||||
|
- **Key Features**: Company verification, official updates, park management
|
||||||
|
|
||||||
|
## Core Workflows
|
||||||
|
|
||||||
|
1. Park Discovery & Information
|
||||||
|
- Geographic search and browsing
|
||||||
|
- Detailed park profiles
|
||||||
|
- Operating hours and details
|
||||||
|
|
||||||
|
2. Ride Management
|
||||||
|
- Comprehensive ride database
|
||||||
|
- Technical specifications
|
||||||
|
- Historical information
|
||||||
|
- Designer attribution
|
||||||
|
|
||||||
|
3. Review System
|
||||||
|
- User-generated content
|
||||||
|
- Media integration
|
||||||
|
- Rating framework
|
||||||
|
- Moderation workflow
|
||||||
|
|
||||||
|
4. Content Moderation
|
||||||
|
- Submission review
|
||||||
|
- Quality control
|
||||||
|
- Content verification
|
||||||
|
- User management
|
||||||
|
|
||||||
|
5. Location Services
|
||||||
|
- Geographic search
|
||||||
|
- Proximity features
|
||||||
|
- Regional categorization
|
||||||
|
|
||||||
|
## Strategic Direction
|
||||||
|
|
||||||
|
### Current Focus
|
||||||
|
1. Content Quality
|
||||||
|
- Robust moderation
|
||||||
|
- Information verification
|
||||||
|
- Rich media support
|
||||||
|
|
||||||
|
2. User Trust
|
||||||
|
- Review authenticity
|
||||||
|
- Company verification
|
||||||
|
- Expert contributions
|
||||||
|
|
||||||
|
3. Data Completeness
|
||||||
|
- Park coverage
|
||||||
|
- Ride information
|
||||||
|
- Historical records
|
||||||
|
|
||||||
|
### Future Roadmap
|
||||||
|
1. Community Features
|
||||||
|
- Enhanced profiles
|
||||||
|
- Contribution recognition
|
||||||
|
- Expert designation
|
||||||
|
|
||||||
|
2. Analytics
|
||||||
|
- Usage patterns
|
||||||
|
- Quality metrics
|
||||||
|
- Engagement tracking
|
||||||
|
|
||||||
|
3. Media
|
||||||
|
- Image improvements
|
||||||
|
- Video support
|
||||||
|
- Virtual tours
|
||||||
165
memory-bank/systemPatterns.md
Normal file
165
memory-bank/systemPatterns.md
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
# System Patterns
|
||||||
|
|
||||||
|
## Architectural Patterns
|
||||||
|
|
||||||
|
### MVT Implementation
|
||||||
|
1. Models
|
||||||
|
- Use abstract base classes for common fields
|
||||||
|
- Implement custom model managers for complex queries
|
||||||
|
- Define clear relationships and constraints
|
||||||
|
- Include field-level validation
|
||||||
|
|
||||||
|
2. Views
|
||||||
|
- Prefer class-based views
|
||||||
|
- Use mixins for shared functionality
|
||||||
|
- Implement proper permission checks
|
||||||
|
- Handle HTMX requests explicitly
|
||||||
|
|
||||||
|
3. Templates
|
||||||
|
- Maintain hierarchy with base templates
|
||||||
|
- Use partial templates for HTMX responses
|
||||||
|
- Implement component-based structure
|
||||||
|
- Follow progressive enhancement
|
||||||
|
|
||||||
|
## Design Patterns
|
||||||
|
|
||||||
|
### Data Access
|
||||||
|
1. Query Patterns
|
||||||
|
- Use select_related() for foreign keys
|
||||||
|
- Implement prefetch_related() for reverse relationships
|
||||||
|
- Create custom model managers
|
||||||
|
- Optimize database queries
|
||||||
|
|
||||||
|
2. Caching Strategy
|
||||||
|
- Cache template fragments
|
||||||
|
- Implement model-level caching
|
||||||
|
- Use Redis for session storage
|
||||||
|
- Cache invalidation rules
|
||||||
|
|
||||||
|
### Frontend Patterns
|
||||||
|
|
||||||
|
1. HTMX Integration
|
||||||
|
```html
|
||||||
|
<!-- Partial Update Pattern -->
|
||||||
|
<div hx-get="/endpoint"
|
||||||
|
hx-trigger="event"
|
||||||
|
hx-target="#target">
|
||||||
|
```
|
||||||
|
|
||||||
|
2. AlpineJS Components
|
||||||
|
```html
|
||||||
|
<!-- State Management Pattern -->
|
||||||
|
<div x-data="{ state: {} }"
|
||||||
|
x-init="state = await fetchData()">
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Tailwind Components
|
||||||
|
```html
|
||||||
|
<!-- Component Structure -->
|
||||||
|
<div class="component-wrapper">
|
||||||
|
<div class="component-header"></div>
|
||||||
|
<div class="component-content"></div>
|
||||||
|
</div>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Authentication Patterns
|
||||||
|
|
||||||
|
### User Management
|
||||||
|
1. Custom User Model
|
||||||
|
- Extended user profiles
|
||||||
|
- Role-based permissions
|
||||||
|
- Activity tracking
|
||||||
|
- Profile customization
|
||||||
|
|
||||||
|
2. Authentication Flow
|
||||||
|
- Login/registration process
|
||||||
|
- Password reset workflow
|
||||||
|
- Email verification
|
||||||
|
- Session management
|
||||||
|
|
||||||
|
## Content Management
|
||||||
|
|
||||||
|
### Moderation Flow
|
||||||
|
1. Submission Process
|
||||||
|
- Content validation
|
||||||
|
- Automatic checks
|
||||||
|
- Manual review queue
|
||||||
|
- Approval workflow
|
||||||
|
|
||||||
|
2. Review System
|
||||||
|
- Rating framework
|
||||||
|
- Media handling
|
||||||
|
- User verification
|
||||||
|
- Content filtering
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### Backend Errors
|
||||||
|
1. Exception Handling
|
||||||
|
```python
|
||||||
|
try:
|
||||||
|
# Operation
|
||||||
|
except SpecificException as e:
|
||||||
|
# Specific handling
|
||||||
|
except Exception as e:
|
||||||
|
# Generic handling
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Response Patterns
|
||||||
|
```python
|
||||||
|
# Success Response
|
||||||
|
return JsonResponse({'status': 'success', 'data': data})
|
||||||
|
|
||||||
|
# Error Response
|
||||||
|
return JsonResponse({'status': 'error', 'message': str(e)})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend Errors
|
||||||
|
1. User Feedback
|
||||||
|
- Toast notifications
|
||||||
|
- Inline validation
|
||||||
|
- Form feedback
|
||||||
|
- Error states
|
||||||
|
|
||||||
|
## Testing Patterns
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
```python
|
||||||
|
class ModelTests(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
# Test setup
|
||||||
|
|
||||||
|
def test_specific_functionality(self):
|
||||||
|
# Test implementation
|
||||||
|
```
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
```python
|
||||||
|
class ViewTests(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.client = Client()
|
||||||
|
|
||||||
|
def test_view_functionality(self):
|
||||||
|
# Test implementation
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Workflows
|
||||||
|
|
||||||
|
### Feature Development
|
||||||
|
1. Planning
|
||||||
|
- Technical specification
|
||||||
|
- Component design
|
||||||
|
- Database schema
|
||||||
|
- API endpoints
|
||||||
|
|
||||||
|
2. Implementation
|
||||||
|
- Model creation
|
||||||
|
- View implementation
|
||||||
|
- Template design
|
||||||
|
- Testing coverage
|
||||||
|
|
||||||
|
3. Review Process
|
||||||
|
- Code review
|
||||||
|
- Testing verification
|
||||||
|
- Documentation update
|
||||||
|
- Deployment planning
|
||||||
157
memory-bank/techContext.md
Normal file
157
memory-bank/techContext.md
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
# Technical Context
|
||||||
|
|
||||||
|
## Architecture Overview
|
||||||
|
|
||||||
|
### Stack Components
|
||||||
|
- **Framework**: Django (MVT Architecture)
|
||||||
|
- **Frontend**: HTMX + AlpineJS + Tailwind CSS
|
||||||
|
- **Database**: Django ORM
|
||||||
|
- **Authentication**: Django Built-in Auth
|
||||||
|
|
||||||
|
## Technical Architecture
|
||||||
|
|
||||||
|
### Backend (Django)
|
||||||
|
1. Core Framework Features
|
||||||
|
- MVT pattern implementation
|
||||||
|
- ORM for data management
|
||||||
|
- Template system
|
||||||
|
- Authentication & permissions
|
||||||
|
- Admin interface
|
||||||
|
- URL routing
|
||||||
|
- Form processing
|
||||||
|
|
||||||
|
2. Data Layer
|
||||||
|
- Models & relationships
|
||||||
|
- Validation rules
|
||||||
|
- Signal handlers
|
||||||
|
- Database migrations
|
||||||
|
|
||||||
|
### Frontend Architecture
|
||||||
|
1. HTMX Integration
|
||||||
|
- Dynamic updates
|
||||||
|
- Partial page renders
|
||||||
|
- Server-side processing
|
||||||
|
- Progressive enhancement
|
||||||
|
|
||||||
|
2. AlpineJS Usage
|
||||||
|
- UI state management
|
||||||
|
- Component behaviors
|
||||||
|
- Event handling
|
||||||
|
- DOM manipulation
|
||||||
|
|
||||||
|
3. Tailwind CSS
|
||||||
|
- Utility-first styling
|
||||||
|
- Custom theme configuration
|
||||||
|
- Responsive design
|
||||||
|
- Dark mode support
|
||||||
|
|
||||||
|
## Integration Patterns
|
||||||
|
|
||||||
|
### Template System
|
||||||
|
1. Structure
|
||||||
|
- Base templates
|
||||||
|
- Model-specific partials
|
||||||
|
- Reusable components
|
||||||
|
- Template inheritance
|
||||||
|
|
||||||
|
2. HTMX Patterns
|
||||||
|
- Partial updates
|
||||||
|
- Server triggers
|
||||||
|
- Event handling
|
||||||
|
- Response processing
|
||||||
|
|
||||||
|
### State Management
|
||||||
|
1. Server-side
|
||||||
|
- Django sessions
|
||||||
|
- Database state
|
||||||
|
- Cache management
|
||||||
|
|
||||||
|
2. Client-side
|
||||||
|
- AlpineJS state
|
||||||
|
- Local storage
|
||||||
|
- HTMX state management
|
||||||
|
|
||||||
|
## Performance Requirements
|
||||||
|
|
||||||
|
### Frontend Targets
|
||||||
|
- First contentful paint < 1.5s
|
||||||
|
- Time to interactive < 2s
|
||||||
|
- Core Web Vitals compliance
|
||||||
|
- Progressive enhancement
|
||||||
|
- Latest 2 versions of major browsers
|
||||||
|
|
||||||
|
### Backend Optimization
|
||||||
|
- Database query optimization
|
||||||
|
- Caching strategy
|
||||||
|
- Asset optimization
|
||||||
|
- API response times
|
||||||
|
|
||||||
|
## Development Environment
|
||||||
|
|
||||||
|
### Required Tools
|
||||||
|
- Python with virtual environment
|
||||||
|
- Node.js (Tailwind build)
|
||||||
|
- Git version control
|
||||||
|
- VSCode IDE
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
- Environment variables
|
||||||
|
- Development settings
|
||||||
|
- Database setup
|
||||||
|
- Media handling
|
||||||
|
|
||||||
|
## Security Framework
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
- Django auth system
|
||||||
|
- Session management
|
||||||
|
- Permission levels
|
||||||
|
- User roles
|
||||||
|
|
||||||
|
### Data Protection
|
||||||
|
- CSRF protection
|
||||||
|
- XSS prevention
|
||||||
|
- SQL injection prevention
|
||||||
|
- Input validation
|
||||||
|
|
||||||
|
## Testing Strategy
|
||||||
|
|
||||||
|
### Backend Testing
|
||||||
|
- Django test runner
|
||||||
|
- Unit tests
|
||||||
|
- Integration tests
|
||||||
|
- Coverage requirements
|
||||||
|
|
||||||
|
### Frontend Testing
|
||||||
|
- Browser testing
|
||||||
|
- Performance metrics
|
||||||
|
- Accessibility testing
|
||||||
|
- User flow validation
|
||||||
|
|
||||||
|
## Deployment Process
|
||||||
|
|
||||||
|
### Environment Setup
|
||||||
|
- Production configuration
|
||||||
|
- Database migration
|
||||||
|
- Static file handling
|
||||||
|
- SSL/TLS setup
|
||||||
|
|
||||||
|
### Monitoring
|
||||||
|
- Error tracking
|
||||||
|
- Performance monitoring
|
||||||
|
- User analytics
|
||||||
|
- System health checks
|
||||||
|
|
||||||
|
## Documentation Requirements
|
||||||
|
|
||||||
|
### Code Documentation
|
||||||
|
- Python docstrings
|
||||||
|
- Type hints
|
||||||
|
- Component documentation
|
||||||
|
- API documentation
|
||||||
|
|
||||||
|
### System Documentation
|
||||||
|
- Setup guides
|
||||||
|
- Architecture docs
|
||||||
|
- Maintenance procedures
|
||||||
|
- Troubleshooting guides
|
||||||
201
memory-bank/workflows/development-process.md
Normal file
201
memory-bank/workflows/development-process.md
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
# Development Workflow
|
||||||
|
|
||||||
|
## Git Workflow
|
||||||
|
|
||||||
|
### Branch Strategy
|
||||||
|
1. Main Branches
|
||||||
|
- `main` - Production code
|
||||||
|
- `develop` - Integration branch
|
||||||
|
|
||||||
|
2. Feature Branches
|
||||||
|
- Format: `feature/description`
|
||||||
|
- Branch from: `develop`
|
||||||
|
- Merge to: `develop`
|
||||||
|
|
||||||
|
3. Bugfix Branches
|
||||||
|
- Format: `bugfix/description`
|
||||||
|
- Branch from: `develop`
|
||||||
|
- Merge to: `develop`
|
||||||
|
|
||||||
|
4. Hotfix Branches
|
||||||
|
- Format: `hotfix/description`
|
||||||
|
- Branch from: `main`
|
||||||
|
- Merge to: `main` and `develop`
|
||||||
|
|
||||||
|
### Commit Guidelines
|
||||||
|
1. Format
|
||||||
|
```
|
||||||
|
type(scope): description
|
||||||
|
|
||||||
|
[optional body]
|
||||||
|
|
||||||
|
[optional footer]
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Types
|
||||||
|
- feat: New feature
|
||||||
|
- fix: Bug fix
|
||||||
|
- docs: Documentation
|
||||||
|
- style: Formatting
|
||||||
|
- refactor: Code restructure
|
||||||
|
- test: Testing
|
||||||
|
- chore: Maintenance
|
||||||
|
|
||||||
|
3. Rules
|
||||||
|
- Present tense verbs
|
||||||
|
- Concise descriptions
|
||||||
|
- Reference issues
|
||||||
|
- Document breaking changes
|
||||||
|
|
||||||
|
## Development Process
|
||||||
|
|
||||||
|
### 1. Feature Development
|
||||||
|
1. Planning
|
||||||
|
- Technical specification
|
||||||
|
- Component design
|
||||||
|
- Database impact
|
||||||
|
- Test strategy
|
||||||
|
|
||||||
|
2. Implementation
|
||||||
|
- Create feature branch
|
||||||
|
- Write tests first
|
||||||
|
- Implement feature
|
||||||
|
- Update documentation
|
||||||
|
|
||||||
|
3. Review
|
||||||
|
- Self-review checklist
|
||||||
|
- Peer code review
|
||||||
|
- Update per feedback
|
||||||
|
- Final verification
|
||||||
|
|
||||||
|
### 2. Testing Requirements
|
||||||
|
|
||||||
|
#### Unit Tests
|
||||||
|
```python
|
||||||
|
# Required for all new code
|
||||||
|
class TestFeature(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
# Setup test data
|
||||||
|
|
||||||
|
def test_functionality(self):
|
||||||
|
# Test core functionality
|
||||||
|
|
||||||
|
def test_edge_cases(self):
|
||||||
|
# Test edge cases
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Integration Tests
|
||||||
|
- API endpoints
|
||||||
|
- User workflows
|
||||||
|
- System integration
|
||||||
|
- Error handling
|
||||||
|
|
||||||
|
#### Coverage Requirements
|
||||||
|
- Minimum 80% coverage
|
||||||
|
- Critical paths 100%
|
||||||
|
- Edge case handling
|
||||||
|
- Error scenarios
|
||||||
|
|
||||||
|
### 3. Code Quality
|
||||||
|
|
||||||
|
#### Linting
|
||||||
|
- Python: flake8
|
||||||
|
- JavaScript: eslint
|
||||||
|
- CSS: stylelint
|
||||||
|
- Templates: djlint
|
||||||
|
|
||||||
|
#### Type Checking
|
||||||
|
- Python: mypy
|
||||||
|
- JavaScript: TypeScript
|
||||||
|
|
||||||
|
#### Documentation
|
||||||
|
- Code comments
|
||||||
|
- Docstrings
|
||||||
|
- README updates
|
||||||
|
- API documentation
|
||||||
|
|
||||||
|
## Deployment Process
|
||||||
|
|
||||||
|
### 1. Pre-deployment
|
||||||
|
- Version bump
|
||||||
|
- Changelog update
|
||||||
|
- Documentation review
|
||||||
|
- Test verification
|
||||||
|
|
||||||
|
### 2. Staging Deployment
|
||||||
|
- Database migrations
|
||||||
|
- Static file collection
|
||||||
|
- Smoke tests
|
||||||
|
- Performance check
|
||||||
|
|
||||||
|
### 3. Production Deployment
|
||||||
|
- Backup database
|
||||||
|
- Apply migrations
|
||||||
|
- Update static files
|
||||||
|
- Health checks
|
||||||
|
|
||||||
|
### 4. Post-deployment
|
||||||
|
- Monitor errors
|
||||||
|
- Performance metrics
|
||||||
|
- User feedback
|
||||||
|
- Rollback plan
|
||||||
|
|
||||||
|
## Review Process
|
||||||
|
|
||||||
|
### 1. Code Review
|
||||||
|
- Style compliance
|
||||||
|
- Test coverage
|
||||||
|
- Documentation
|
||||||
|
- Performance impact
|
||||||
|
|
||||||
|
### 2. Architecture Review
|
||||||
|
- Design patterns
|
||||||
|
- Scalability
|
||||||
|
- Security
|
||||||
|
- Maintainability
|
||||||
|
|
||||||
|
### 3. Security Review
|
||||||
|
- Authentication
|
||||||
|
- Authorization
|
||||||
|
- Data protection
|
||||||
|
- Input validation
|
||||||
|
|
||||||
|
## Quality Assurance
|
||||||
|
|
||||||
|
### 1. Testing Strategy
|
||||||
|
- Unit testing
|
||||||
|
- Integration testing
|
||||||
|
- End-to-end testing
|
||||||
|
- Performance testing
|
||||||
|
|
||||||
|
### 2. Performance Standards
|
||||||
|
- Page load times
|
||||||
|
- Database queries
|
||||||
|
- API response times
|
||||||
|
- Resource usage
|
||||||
|
|
||||||
|
### 3. Security Standards
|
||||||
|
- Authentication
|
||||||
|
- Authorization
|
||||||
|
- Data encryption
|
||||||
|
- Input validation
|
||||||
|
|
||||||
|
## Monitoring and Maintenance
|
||||||
|
|
||||||
|
### 1. Error Tracking
|
||||||
|
- Exception monitoring
|
||||||
|
- Log analysis
|
||||||
|
- User reports
|
||||||
|
- Performance alerts
|
||||||
|
|
||||||
|
### 2. Performance Monitoring
|
||||||
|
- Response times
|
||||||
|
- Resource usage
|
||||||
|
- Database performance
|
||||||
|
- Cache effectiveness
|
||||||
|
|
||||||
|
### 3. User Feedback
|
||||||
|
- Bug reports
|
||||||
|
- Feature requests
|
||||||
|
- Performance issues
|
||||||
|
- UX feedback
|
||||||
@@ -5,27 +5,37 @@
|
|||||||
|
|
||||||
{% block extra_css %}
|
{% block extra_css %}
|
||||||
<style>
|
<style>
|
||||||
|
/* Base Styles */
|
||||||
|
:root {
|
||||||
|
--loading-gradient: linear-gradient(90deg, var(--tw-gradient-from) 0%, var(--tw-gradient-to) 50%, var(--tw-gradient-from) 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive Layout */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.grid-cols-responsive {
|
||||||
|
@apply grid-cols-1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons {
|
||||||
|
@apply flex-col;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons > * {
|
||||||
|
@apply w-full justify-center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Form Elements */
|
/* Form Elements */
|
||||||
.form-select {
|
.form-select {
|
||||||
@apply rounded-lg border-gray-700 focus:border-blue-500 focus:ring-2 focus:ring-blue-500 bg-gray-800 text-gray-300 transition-colors duration-200;
|
@apply rounded-lg border-gray-700 focus:border-blue-500 focus:ring-2 focus:ring-blue-500 bg-gray-800 text-gray-300 transition-colors duration-200;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Transitions */
|
/* State Management */
|
||||||
[x-cloak] {
|
[x-cloak] {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fade-enter-active,
|
/* Loading States */
|
||||||
.fade-leave-active {
|
|
||||||
@apply transition-all duration-200;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter-from,
|
|
||||||
.fade-leave-to {
|
|
||||||
@apply opacity-0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* HTMX Loading States */
|
|
||||||
.htmx-request .htmx-indicator {
|
.htmx-request .htmx-indicator {
|
||||||
@apply opacity-100;
|
@apply opacity-100;
|
||||||
}
|
}
|
||||||
@@ -38,31 +48,131 @@
|
|||||||
@apply opacity-0 transition-opacity duration-200;
|
@apply opacity-0 transition-opacity duration-200;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Animations */
|
/* Skeleton Loading Animation */
|
||||||
@keyframes spin {
|
.animate-pulse {
|
||||||
to {
|
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
|
||||||
transform: rotate(360deg);
|
}
|
||||||
|
|
||||||
|
@keyframes pulse {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: .5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.animate-spin {
|
/* Transitions */
|
||||||
animation: spin 1s linear infinite;
|
.fade-enter-active,
|
||||||
|
.fade-leave-active {
|
||||||
|
@apply transition-all duration-200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fade-enter-from,
|
||||||
|
.fade-leave-to {
|
||||||
|
@apply opacity-0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Custom Animations */
|
||||||
|
@keyframes shimmer {
|
||||||
|
100% {
|
||||||
|
transform: translateX(100%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-shimmer {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-shimmer::after {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
transform: translateX(-100%);
|
||||||
|
background-image: var(--loading-gradient);
|
||||||
|
animation: shimmer 2s infinite;
|
||||||
|
content: '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Accessibility Enhancements */
|
||||||
|
:focus-visible {
|
||||||
|
@apply outline-none ring-2 ring-blue-500 ring-offset-2 ring-offset-white dark:ring-offset-gray-900;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.animate-shimmer::after {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-pulse {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Touch Device Optimizations */
|
||||||
|
@media (hover: none) {
|
||||||
|
.hover\:shadow-md {
|
||||||
|
@apply shadow-sm;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-buttons > * {
|
||||||
|
@apply active:transform active:scale-95;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Dark Mode Optimizations */
|
||||||
|
.dark .animate-shimmer::after {
|
||||||
|
--tw-gradient-from: rgba(31, 41, 55, 0);
|
||||||
|
--tw-gradient-to: rgba(31, 41, 55, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Error States */
|
||||||
|
.error-shake {
|
||||||
|
animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
|
||||||
|
transform: translate3d(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes shake {
|
||||||
|
10%, 90% { transform: translate3d(-1px, 0, 0); }
|
||||||
|
20%, 80% { transform: translate3d(2px, 0, 0); }
|
||||||
|
30%, 50%, 70% { transform: translate3d(-4px, 0, 0); }
|
||||||
|
40%, 60% { transform: translate3d(4px, 0, 0); }
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container max-w-6xl px-4 py-6 mx-auto">
|
<div class="container max-w-6xl px-4 py-6 mx-auto">
|
||||||
<div id="dashboard-content" class="relative transition-opacity duration-200">
|
<div id="dashboard-content" class="relative transition-all duration-200">
|
||||||
{% block moderation_content %}
|
{% block moderation_content %}
|
||||||
{% include "moderation/partials/dashboard_content.html" %}
|
{% include "moderation/partials/dashboard_content.html" %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
<div id="loading-indicator"
|
<!-- Loading Skeleton -->
|
||||||
class="absolute inset-0 flex items-center justify-center rounded-lg htmx-indicator bg-gray-900/80">
|
<div class="absolute inset-0 htmx-indicator" id="loading-skeleton">
|
||||||
<div class="flex items-center p-6 space-x-4">
|
{% include "moderation/partials/loading_skeleton.html" %}
|
||||||
<div class="w-8 h-8 border-4 border-blue-500 rounded-full animate-spin border-t-transparent"></div>
|
</div>
|
||||||
<span class="text-gray-300">Loading...</span>
|
|
||||||
|
<!-- Error State -->
|
||||||
|
<div class="absolute inset-0 hidden" id="error-state">
|
||||||
|
<div class="flex flex-col items-center justify-center h-full p-6 space-y-4 text-center">
|
||||||
|
<div class="p-4 text-red-500 bg-red-100 rounded-full dark:bg-red-900/40">
|
||||||
|
<i class="text-4xl fas fa-exclamation-circle"></i>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-lg font-medium text-red-600 dark:text-red-400">
|
||||||
|
Something went wrong
|
||||||
|
</h3>
|
||||||
|
<p class="max-w-md text-gray-600 dark:text-gray-400" id="error-message">
|
||||||
|
There was a problem loading the content. Please try again.
|
||||||
|
</p>
|
||||||
|
<button class="px-4 py-2 font-medium text-white transition-all duration-200 bg-red-600 rounded-lg hover:bg-red-500 dark:bg-red-700 dark:hover:bg-red-600"
|
||||||
|
onclick="window.location.reload()">
|
||||||
|
<i class="mr-2 fas fa-sync-alt"></i>
|
||||||
|
Retry
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -71,20 +181,116 @@
|
|||||||
|
|
||||||
{% block extra_js %}
|
{% block extra_js %}
|
||||||
<script>
|
<script>
|
||||||
// Configure HTMX to include CSRF token in all requests
|
// HTMX Configuration and Enhancements
|
||||||
document.body.addEventListener('htmx:configRequest', function(evt) {
|
document.body.addEventListener('htmx:configRequest', function(evt) {
|
||||||
evt.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
|
evt.detail.headers['X-CSRFToken'] = '{{ csrf_token }}';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Loading and Error State Management
|
||||||
|
const dashboard = {
|
||||||
|
content: document.getElementById('dashboard-content'),
|
||||||
|
skeleton: document.getElementById('loading-skeleton'),
|
||||||
|
errorState: document.getElementById('error-state'),
|
||||||
|
errorMessage: document.getElementById('error-message'),
|
||||||
|
|
||||||
|
showLoading() {
|
||||||
|
this.content.setAttribute('aria-busy', 'true');
|
||||||
|
this.content.style.opacity = '0';
|
||||||
|
this.errorState.classList.add('hidden');
|
||||||
|
},
|
||||||
|
|
||||||
|
hideLoading() {
|
||||||
|
this.content.setAttribute('aria-busy', 'false');
|
||||||
|
this.content.style.opacity = '1';
|
||||||
|
},
|
||||||
|
|
||||||
|
showError(message) {
|
||||||
|
this.errorState.classList.remove('hidden');
|
||||||
|
this.errorMessage.textContent = message || 'There was a problem loading the content. Please try again.';
|
||||||
|
// Announce error to screen readers
|
||||||
|
this.errorMessage.setAttribute('role', 'alert');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Enhanced HTMX Event Handlers
|
||||||
document.body.addEventListener('htmx:beforeRequest', function(evt) {
|
document.body.addEventListener('htmx:beforeRequest', function(evt) {
|
||||||
if (evt.detail.target.id === 'dashboard-content') {
|
if (evt.detail.target.id === 'dashboard-content') {
|
||||||
evt.detail.target.style.opacity = '0.5';
|
dashboard.showLoading();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
document.body.addEventListener('htmx:afterOnLoad', function(evt) {
|
document.body.addEventListener('htmx:afterOnLoad', function(evt) {
|
||||||
if (evt.detail.target.id === 'dashboard-content') {
|
if (evt.detail.target.id === 'dashboard-content') {
|
||||||
evt.detail.target.style.opacity = '1';
|
dashboard.hideLoading();
|
||||||
|
// Reset focus for accessibility
|
||||||
|
const firstFocusable = evt.detail.target.querySelector('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
|
||||||
|
if (firstFocusable) {
|
||||||
|
firstFocusable.focus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
document.body.addEventListener('htmx:responseError', function(evt) {
|
||||||
|
if (evt.detail.target.id === 'dashboard-content') {
|
||||||
|
dashboard.showError(evt.detail.error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Search Input Debouncing
|
||||||
|
function debounce(func, wait) {
|
||||||
|
let timeout;
|
||||||
|
return function executedFunction(...args) {
|
||||||
|
const later = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
func(...args);
|
||||||
|
};
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(later, wait);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply debouncing to search inputs
|
||||||
|
document.querySelectorAll('[data-search]').forEach(input => {
|
||||||
|
const originalSearch = () => {
|
||||||
|
htmx.trigger(input, 'input');
|
||||||
|
};
|
||||||
|
const debouncedSearch = debounce(originalSearch, 300);
|
||||||
|
|
||||||
|
input.addEventListener('input', (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
debouncedSearch();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Virtual Scrolling for Large Lists
|
||||||
|
const observerOptions = {
|
||||||
|
root: null,
|
||||||
|
rootMargin: '100px',
|
||||||
|
threshold: 0.1
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadMoreContent = (entries, observer) => {
|
||||||
|
entries.forEach(entry => {
|
||||||
|
if (entry.isIntersecting && !entry.target.classList.contains('loading')) {
|
||||||
|
entry.target.classList.add('loading');
|
||||||
|
htmx.trigger(entry.target, 'intersect');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const observer = new IntersectionObserver(loadMoreContent, observerOptions);
|
||||||
|
document.querySelectorAll('[data-infinite-scroll]').forEach(el => observer.observe(el));
|
||||||
|
|
||||||
|
// Keyboard Navigation Enhancement
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
const openModals = document.querySelectorAll('[x-show="showNotes"]');
|
||||||
|
openModals.forEach(modal => {
|
||||||
|
const alpineData = modal.__x.$data;
|
||||||
|
if (alpineData.showNotes) {
|
||||||
|
alpineData.showNotes = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -56,44 +56,93 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="p-6 bg-white border rounded-lg dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50">
|
<div class="p-6 bg-white border rounded-lg dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50">
|
||||||
<form class="flex flex-wrap items-end gap-4 mb-6"
|
<form class="mb-6"
|
||||||
|
x-data="{ showFilters: false }"
|
||||||
hx-get="{% url 'moderation:submission_list' %}"
|
hx-get="{% url 'moderation:submission_list' %}"
|
||||||
hx-target="#dashboard-content"
|
hx-target="#dashboard-content"
|
||||||
hx-trigger="change"
|
hx-trigger="change from:select"
|
||||||
hx-push-url="true">
|
hx-push-url="true"
|
||||||
|
aria-label="Submission filters">
|
||||||
|
|
||||||
<div class="flex-1 min-w-[200px]">
|
<!-- Mobile Filter Toggle -->
|
||||||
<label class="block mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">
|
<button type="button"
|
||||||
Submission Type
|
class="flex items-center w-full gap-2 p-3 mb-4 font-medium text-left text-gray-700 transition-colors duration-200 bg-gray-100 rounded-lg md:hidden dark:text-gray-300 dark:bg-gray-900"
|
||||||
</label>
|
@click="showFilters = !showFilters"
|
||||||
<select name="submission_type" class="w-full px-3 py-2 text-gray-900 bg-white border rounded-lg dark:text-gray-300 dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50 focus:outline-none focus:ring-2 focus:ring-blue-500">
|
:aria-expanded="showFilters"
|
||||||
<option value="">All Submissions</option>
|
aria-controls="filter-controls">
|
||||||
<option value="text" {% if request.GET.submission_type == 'text' %}selected{% endif %}>Text Submissions</option>
|
<i class="fas" :class="showFilters ? 'fa-chevron-up' : 'fa-chevron-down'"></i>
|
||||||
<option value="photo" {% if request.GET.submission_type == 'photo' %}selected{% endif %}>Photo Submissions</option>
|
<span>Filter Options</span>
|
||||||
</select>
|
<span class="flex items-center ml-auto space-x-1 text-sm text-gray-500 dark:text-gray-400">
|
||||||
</div>
|
<span class="active-filters">{{ request.GET|length }} active</span>
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
<div class="flex-1 min-w-[200px]">
|
<!-- Filter Controls -->
|
||||||
<label class="block mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">
|
<div id="filter-controls"
|
||||||
Type
|
class="grid gap-4 transition-all duration-200 md:grid-cols-3"
|
||||||
</label>
|
:class="{'hidden md:grid': !showFilters, 'grid': showFilters}"
|
||||||
<select name="type" class="w-full px-3 py-2 text-gray-900 bg-white border rounded-lg dark:text-gray-300 dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50 focus:outline-none focus:ring-2 focus:ring-blue-500">
|
role="group"
|
||||||
<option value="">All Types</option>
|
aria-label="Filter controls">
|
||||||
<option value="CREATE" {% if request.GET.type == 'CREATE' %}selected{% endif %}>New Submissions</option>
|
|
||||||
<option value="EDIT" {% if request.GET.type == 'EDIT' %}selected{% endif %}>Edit Submissions</option>
|
<div class="relative">
|
||||||
</select>
|
<label id="submission-type-label"
|
||||||
</div>
|
class="block mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">
|
||||||
|
Submission Type
|
||||||
|
</label>
|
||||||
|
<select name="submission_type"
|
||||||
|
aria-labelledby="submission-type-label"
|
||||||
|
class="w-full px-3 py-2 text-gray-900 bg-white border rounded-lg dark:text-gray-300 dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
@change="$dispatch('filter-changed')">
|
||||||
|
<option value="">All Submissions</option>
|
||||||
|
<option value="text" {% if request.GET.submission_type == 'text' %}selected{% endif %}>Text Submissions</option>
|
||||||
|
<option value="photo" {% if request.GET.submission_type == 'photo' %}selected{% endif %}>Photo Submissions</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex-1 min-w-[200px]">
|
<div class="relative">
|
||||||
<label class="block mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">
|
<label id="type-label"
|
||||||
Content Type
|
class="block mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">
|
||||||
</label>
|
Type
|
||||||
<select name="content_type" class="w-full px-3 py-2 text-gray-900 bg-white border rounded-lg dark:text-gray-300 dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50 focus:outline-none focus:ring-2 focus:ring-blue-500">
|
</label>
|
||||||
<option value="">All Content</option>
|
<select name="type"
|
||||||
<option value="park" {% if request.GET.content_type == 'park' %}selected{% endif %}>Parks</option>
|
aria-labelledby="type-label"
|
||||||
<option value="ride" {% if request.GET.content_type == 'ride' %}selected{% endif %}>Rides</option>
|
class="w-full px-3 py-2 text-gray-900 bg-white border rounded-lg dark:text-gray-300 dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
<option value="company" {% if request.GET.content_type == 'company' %}selected{% endif %}>Companies</option>
|
@change="$dispatch('filter-changed')">
|
||||||
</select>
|
<option value="">All Types</option>
|
||||||
|
<option value="CREATE" {% if request.GET.type == 'CREATE' %}selected{% endif %}>New Submissions</option>
|
||||||
|
<option value="EDIT" {% if request.GET.type == 'EDIT' %}selected{% endif %}>Edit Submissions</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="relative">
|
||||||
|
<label id="content-type-label"
|
||||||
|
class="block mb-2 text-sm font-medium text-gray-600 dark:text-gray-400">
|
||||||
|
Content Type
|
||||||
|
</label>
|
||||||
|
<select name="content_type"
|
||||||
|
aria-labelledby="content-type-label"
|
||||||
|
class="w-full px-3 py-2 text-gray-900 bg-white border rounded-lg dark:text-gray-300 dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50 focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||||||
|
@change="$dispatch('filter-changed')">
|
||||||
|
<option value="">All Content</option>
|
||||||
|
<option value="park" {% if request.GET.content_type == 'park' %}selected{% endif %}>Parks</option>
|
||||||
|
<option value="ride" {% if request.GET.content_type == 'ride' %}selected{% endif %}>Rides</option>
|
||||||
|
<option value="company" {% if request.GET.content_type == 'company' %}selected{% endif %}>Companies</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Applied Filters Summary -->
|
||||||
|
<div class="hidden pt-2 md:col-span-3 md:block">
|
||||||
|
<div class="flex flex-wrap gap-2"
|
||||||
|
x-show="$store.filters.hasActiveFilters"
|
||||||
|
x-cloak>
|
||||||
|
<template x-for="filter in $store.filters.active" :key="filter.name">
|
||||||
|
<span class="inline-flex items-center px-2 py-1 text-sm bg-blue-100 rounded dark:bg-blue-900/40">
|
||||||
|
<span class="mr-1 text-blue-700 dark:text-blue-300" x-text="filter.label + ':'"></span>
|
||||||
|
<span class="font-medium text-blue-900 dark:text-blue-100" x-text="filter.value"></span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
|||||||
129
templates/moderation/partials/filters_store.html
Normal file
129
templates/moderation/partials/filters_store.html
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
{% comment %}
|
||||||
|
This template contains the Alpine.js store for managing filter state in the moderation dashboard
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.addEventListener('alpine:init', () => {
|
||||||
|
Alpine.store('filters', {
|
||||||
|
active: [],
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this.updateActiveFilters();
|
||||||
|
|
||||||
|
// Listen for filter changes
|
||||||
|
window.addEventListener('filter-changed', () => {
|
||||||
|
this.updateActiveFilters();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updateActiveFilters() {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
this.active = [];
|
||||||
|
|
||||||
|
// Submission Type
|
||||||
|
if (urlParams.has('submission_type')) {
|
||||||
|
this.active.push({
|
||||||
|
name: 'submission_type',
|
||||||
|
label: 'Submission',
|
||||||
|
value: this.getSubmissionTypeLabel(urlParams.get('submission_type'))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Type
|
||||||
|
if (urlParams.has('type')) {
|
||||||
|
this.active.push({
|
||||||
|
name: 'type',
|
||||||
|
label: 'Type',
|
||||||
|
value: this.getTypeLabel(urlParams.get('type'))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Content Type
|
||||||
|
if (urlParams.has('content_type')) {
|
||||||
|
this.active.push({
|
||||||
|
name: 'content_type',
|
||||||
|
label: 'Content',
|
||||||
|
value: this.getContentTypeLabel(urlParams.get('content_type'))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
getSubmissionTypeLabel(value) {
|
||||||
|
const labels = {
|
||||||
|
'text': 'Text',
|
||||||
|
'photo': 'Photo'
|
||||||
|
};
|
||||||
|
return labels[value] || value;
|
||||||
|
},
|
||||||
|
|
||||||
|
getTypeLabel(value) {
|
||||||
|
const labels = {
|
||||||
|
'CREATE': 'New',
|
||||||
|
'EDIT': 'Edit'
|
||||||
|
};
|
||||||
|
return labels[value] || value;
|
||||||
|
},
|
||||||
|
|
||||||
|
getContentTypeLabel(value) {
|
||||||
|
const labels = {
|
||||||
|
'park': 'Parks',
|
||||||
|
'ride': 'Rides',
|
||||||
|
'company': 'Companies'
|
||||||
|
};
|
||||||
|
return labels[value] || value;
|
||||||
|
},
|
||||||
|
|
||||||
|
get hasActiveFilters() {
|
||||||
|
return this.active.length > 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
const form = document.querySelector('form[hx-get]');
|
||||||
|
if (form) {
|
||||||
|
form.querySelectorAll('select').forEach(select => {
|
||||||
|
select.value = '';
|
||||||
|
});
|
||||||
|
form.dispatchEvent(new Event('change'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Accessibility Helpers
|
||||||
|
announceFilterChange() {
|
||||||
|
const message = this.hasActiveFilters
|
||||||
|
? `Applied filters: ${this.active.map(f => f.label + ': ' + f.value).join(', ')}`
|
||||||
|
: 'All filters cleared';
|
||||||
|
|
||||||
|
const announcement = document.createElement('div');
|
||||||
|
announcement.setAttribute('role', 'status');
|
||||||
|
announcement.setAttribute('aria-live', 'polite');
|
||||||
|
announcement.className = 'sr-only';
|
||||||
|
announcement.textContent = message;
|
||||||
|
|
||||||
|
document.body.appendChild(announcement);
|
||||||
|
setTimeout(() => announcement.remove(), 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Watch for filter changes and update URL params
|
||||||
|
document.addEventListener('filter-changed', (e) => {
|
||||||
|
const form = e.target.closest('form');
|
||||||
|
if (!form) return;
|
||||||
|
|
||||||
|
const formData = new FormData(form);
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
|
||||||
|
for (let [key, value] of formData.entries()) {
|
||||||
|
if (value) {
|
||||||
|
params.append(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update URL without page reload
|
||||||
|
const newUrl = window.location.pathname + (params.toString() ? '?' + params.toString() : '');
|
||||||
|
window.history.pushState({}, '', newUrl);
|
||||||
|
|
||||||
|
// Announce changes for screen readers
|
||||||
|
Alpine.store('filters').announceFilterChange();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
66
templates/moderation/partials/loading_skeleton.html
Normal file
66
templates/moderation/partials/loading_skeleton.html
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
{% load static %}
|
||||||
|
|
||||||
|
<div class="animate-pulse">
|
||||||
|
<!-- Filter Bar Skeleton -->
|
||||||
|
<div class="flex items-center justify-between p-4 mb-6 bg-white border rounded-lg dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50">
|
||||||
|
<div class="flex items-center space-x-4">
|
||||||
|
{% for i in "1234" %}
|
||||||
|
<div class="w-24 h-10 bg-gray-200 rounded-lg dark:bg-gray-700"></div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
<div class="w-24 h-10 bg-gray-200 rounded-lg dark:bg-gray-700"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Filter Form Skeleton -->
|
||||||
|
<div class="p-6 mb-6 bg-white border rounded-lg dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50">
|
||||||
|
<div class="flex flex-wrap items-end gap-4">
|
||||||
|
{% for i in "123" %}
|
||||||
|
<div class="flex-1 min-w-[200px] space-y-2">
|
||||||
|
<div class="w-24 h-4 bg-gray-200 rounded dark:bg-gray-700"></div>
|
||||||
|
<div class="w-full h-10 bg-gray-200 rounded-lg dark:bg-gray-700"></div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Submission List Skeleton -->
|
||||||
|
{% for i in "123" %}
|
||||||
|
<div class="p-6 mb-4 bg-white border rounded-lg dark:bg-gray-800 border-gray-200/50 dark:border-gray-700/50">
|
||||||
|
<div class="grid grid-cols-1 gap-6 md:grid-cols-3">
|
||||||
|
<!-- Left Column -->
|
||||||
|
<div class="space-y-4 md:col-span-1">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div class="w-24 h-6 bg-gray-200 rounded-lg dark:bg-gray-700"></div>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-3">
|
||||||
|
{% for i in "1234" %}
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<div class="w-5 h-5 bg-gray-200 rounded dark:bg-gray-700"></div>
|
||||||
|
<div class="w-32 h-4 bg-gray-200 rounded dark:bg-gray-700"></div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Right Column -->
|
||||||
|
<div class="md:col-span-2">
|
||||||
|
{% for i in "12" %}
|
||||||
|
<div class="p-4 mb-4 bg-gray-100 border rounded-lg dark:bg-gray-900 border-gray-200/50 dark:border-gray-700/50">
|
||||||
|
<div class="w-24 h-4 mb-2 bg-gray-200 rounded dark:bg-gray-700"></div>
|
||||||
|
<div class="w-full h-4 bg-gray-200 rounded dark:bg-gray-700"></div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-3 mt-4 md:grid-cols-2">
|
||||||
|
{% for i in "1234" %}
|
||||||
|
<div class="p-4 bg-gray-100 border rounded-lg dark:bg-gray-900 border-gray-200/50 dark:border-gray-700/50">
|
||||||
|
<div class="w-24 h-4 mb-2 bg-gray-200 rounded dark:bg-gray-700"></div>
|
||||||
|
<div class="w-full h-4 bg-gray-200 rounded dark:bg-gray-700"></div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
@@ -162,7 +162,7 @@ AUTHENTICATION_BACKENDS = [
|
|||||||
SITE_ID = 1
|
SITE_ID = 1
|
||||||
ACCOUNT_EMAIL_REQUIRED = True
|
ACCOUNT_EMAIL_REQUIRED = True
|
||||||
ACCOUNT_USERNAME_REQUIRED = True
|
ACCOUNT_USERNAME_REQUIRED = True
|
||||||
ACCOUNT_AUTHENTICATION_METHOD = "username_email"
|
ACCOUNT_LOGIN_METHODS = {'email', 'username'}
|
||||||
ACCOUNT_EMAIL_VERIFICATION = "optional"
|
ACCOUNT_EMAIL_VERIFICATION = "optional"
|
||||||
LOGIN_REDIRECT_URL = "/"
|
LOGIN_REDIRECT_URL = "/"
|
||||||
ACCOUNT_LOGOUT_REDIRECT_URL = "/"
|
ACCOUNT_LOGOUT_REDIRECT_URL = "/"
|
||||||
|
|||||||
Reference in New Issue
Block a user