После рефакторинга
This commit is contained in:
148
dbapp/mainapp/static/js/SORTING_README.md
Normal file
148
dbapp/mainapp/static/js/SORTING_README.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# Sorting Functionality Documentation
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes the centralized sorting functionality implemented for table columns across the Django application.
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
### Created Files:
|
||||
1. **`dbapp/mainapp/static/js/sorting.js`** - Main sorting JavaScript library
|
||||
2. **`dbapp/mainapp/static/js/sorting-test.html`** - Test page for manual verification
|
||||
|
||||
### Modified Files:
|
||||
1. **`dbapp/mainapp/templates/mainapp/base.html`** - Added sorting.js script include
|
||||
2. **`dbapp/mainapp/templates/mainapp/components/_sort_header.html`** - Removed inline script, added data attributes
|
||||
|
||||
## Features
|
||||
|
||||
### 1. Sort Toggle Logic
|
||||
- **First click**: Sort ascending (field)
|
||||
- **Second click**: Sort descending (-field)
|
||||
- **Third click**: Sort ascending again (cycles back)
|
||||
|
||||
### 2. URL Parameter Management
|
||||
- Preserves all existing GET parameters (search, filters, etc.)
|
||||
- Automatically resets page number to 1 when sorting changes
|
||||
- Updates the `sort` parameter in the URL
|
||||
|
||||
### 3. Visual Indicators
|
||||
- Shows up arrow (↑) for ascending sort
|
||||
- Shows down arrow (↓) for descending sort
|
||||
- Automatically initializes indicators on page load
|
||||
- Adds `sort-active` class to currently sorted column
|
||||
|
||||
## Usage
|
||||
|
||||
### In Templates
|
||||
|
||||
Use the `_sort_header.html` component in your table headers:
|
||||
|
||||
```django
|
||||
<thead class="table-dark sticky-top">
|
||||
<tr>
|
||||
<th>{% include 'mainapp/components/_sort_header.html' with field='id' label='ID' current_sort=sort %}</th>
|
||||
<th>{% include 'mainapp/components/_sort_header.html' with field='name' label='Название' current_sort=sort %}</th>
|
||||
<th>{% include 'mainapp/components/_sort_header.html' with field='created_at' label='Дата создания' current_sort=sort %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
```
|
||||
|
||||
### In Views
|
||||
|
||||
Pass the current sort parameter to the template context:
|
||||
|
||||
```python
|
||||
def get(self, request):
|
||||
sort = request.GET.get('sort', '-id') # Default sort
|
||||
|
||||
# Validate allowed sorts
|
||||
allowed_sorts = ['id', '-id', 'name', '-name', 'created_at', '-created_at']
|
||||
if sort not in allowed_sorts:
|
||||
sort = '-id'
|
||||
|
||||
# Apply sorting
|
||||
queryset = Model.objects.all().order_by(sort)
|
||||
|
||||
context = {
|
||||
'sort': sort,
|
||||
'objects': queryset,
|
||||
# ... other context
|
||||
}
|
||||
return render(request, 'template.html', context)
|
||||
```
|
||||
|
||||
## JavaScript API
|
||||
|
||||
### Functions
|
||||
|
||||
#### `updateSort(field)`
|
||||
Updates the sort parameter and reloads the page.
|
||||
|
||||
**Parameters:**
|
||||
- `field` (string): The field name to sort by
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
updateSort('created_at'); // Sort by created_at ascending
|
||||
```
|
||||
|
||||
#### `getCurrentSort()`
|
||||
Gets the current sort field and direction from URL.
|
||||
|
||||
**Returns:**
|
||||
- Object with `field` and `direction` properties
|
||||
- `direction` can be 'asc', 'desc', or null
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
const sort = getCurrentSort();
|
||||
console.log(sort.field); // 'created_at'
|
||||
console.log(sort.direction); // 'asc' or 'desc'
|
||||
```
|
||||
|
||||
#### `initializeSortIndicators()`
|
||||
Automatically called on page load to show current sort state.
|
||||
|
||||
## Requirements Satisfied
|
||||
|
||||
This implementation satisfies the following requirements from the specification:
|
||||
|
||||
- **5.1**: Supports ascending and descending order for sortable columns
|
||||
- **5.2**: Toggles between ascending, descending when clicking column headers
|
||||
- **5.3**: Displays visual indicators (arrow icons) showing sort direction
|
||||
- **5.5**: Preserves sort state in URL parameters during navigation
|
||||
- **5.6**: Preserves other active filters and resets pagination when sorting
|
||||
|
||||
## Testing
|
||||
|
||||
### Manual Testing
|
||||
|
||||
1. Open `dbapp/mainapp/static/js/sorting-test.html` in a browser
|
||||
2. Click column headers to test sorting
|
||||
3. Verify URL updates correctly
|
||||
4. Add query parameters (e.g., ?page=5&search=test) and verify they're preserved
|
||||
|
||||
### Integration Testing
|
||||
|
||||
Test in actual Django views:
|
||||
1. Navigate to any list view (sources, objitems, transponders)
|
||||
2. Click column headers to sort
|
||||
3. Verify data is sorted correctly
|
||||
4. Apply filters and verify they're preserved when sorting
|
||||
5. Navigate to page 2+, then sort - verify it resets to page 1
|
||||
|
||||
## Browser Compatibility
|
||||
|
||||
- Modern browsers supporting ES6 (URLSearchParams)
|
||||
- Chrome 49+
|
||||
- Firefox 44+
|
||||
- Safari 10.1+
|
||||
- Edge 17+
|
||||
|
||||
## Notes
|
||||
|
||||
- The sorting.js file is loaded with `defer` attribute for better performance
|
||||
- All GET parameters are preserved except `page` which is reset to 1
|
||||
- The function is globally available and can be called from any template
|
||||
- Sort indicators are automatically initialized on page load
|
||||
91
dbapp/mainapp/static/js/sorting-test.html
Normal file
91
dbapp/mainapp/static/js/sorting-test.html
Normal file
@@ -0,0 +1,91 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Sorting Test</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css" rel="stylesheet">
|
||||
</head>
|
||||
<body>
|
||||
<div class="container mt-5">
|
||||
<h1>Sorting Functionality Test</h1>
|
||||
|
||||
<div class="alert alert-info">
|
||||
<strong>Current URL:</strong> <span id="currentUrl"></span>
|
||||
</div>
|
||||
|
||||
<table class="table table-striped">
|
||||
<thead class="table-dark">
|
||||
<tr>
|
||||
<th>
|
||||
<a href="javascript:void(0)"
|
||||
onclick="updateSort('id')"
|
||||
class="text-white text-decoration-none"
|
||||
data-sort-field="id">
|
||||
ID
|
||||
<i class="bi bi-arrow-up sort-icon" style="display: none;"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="javascript:void(0)"
|
||||
onclick="updateSort('name')"
|
||||
class="text-white text-decoration-none"
|
||||
data-sort-field="name">
|
||||
Name
|
||||
<i class="bi bi-arrow-up sort-icon" style="display: none;"></i>
|
||||
</a>
|
||||
</th>
|
||||
<th>
|
||||
<a href="javascript:void(0)"
|
||||
onclick="updateSort('created_at')"
|
||||
class="text-white text-decoration-none"
|
||||
data-sort-field="created_at">
|
||||
Created At
|
||||
<i class="bi bi-arrow-up sort-icon" style="display: none;"></i>
|
||||
</a>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>1</td>
|
||||
<td>Test Item 1</td>
|
||||
<td>2024-01-01</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>2</td>
|
||||
<td>Test Item 2</td>
|
||||
<td>2024-01-02</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<h5>Test Instructions:</h5>
|
||||
<ol>
|
||||
<li>Click on any column header (ID, Name, or Created At)</li>
|
||||
<li>The URL should update with ?sort=field_name</li>
|
||||
<li>Click again to toggle to descending (?sort=-field_name)</li>
|
||||
<li>Click a third time to toggle back to ascending</li>
|
||||
<li>Add ?page=5 to the URL and click a header - page should reset to 1</li>
|
||||
<li>Add ?search=test to the URL and click a header - search should be preserved</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="sorting.js"></script>
|
||||
<script>
|
||||
// Display current URL
|
||||
function updateUrlDisplay() {
|
||||
document.getElementById('currentUrl').textContent = window.location.href;
|
||||
}
|
||||
updateUrlDisplay();
|
||||
|
||||
// Update URL display on page load
|
||||
window.addEventListener('load', updateUrlDisplay);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
106
dbapp/mainapp/static/js/sorting.js
Normal file
106
dbapp/mainapp/static/js/sorting.js
Normal file
@@ -0,0 +1,106 @@
|
||||
/**
|
||||
* Sorting functionality for table columns
|
||||
* Handles toggling between ascending, descending, and no sort
|
||||
* Preserves other GET parameters and resets pagination
|
||||
*/
|
||||
|
||||
/**
|
||||
* Updates the sort parameter in the URL and reloads the page
|
||||
* @param {string} field - The field name to sort by
|
||||
*/
|
||||
function updateSort(field) {
|
||||
// Get current URL parameters
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const currentSort = urlParams.get('sort');
|
||||
|
||||
let newSort;
|
||||
|
||||
// Toggle sort direction logic:
|
||||
// 1. If not sorted by this field -> sort ascending (field)
|
||||
// 2. If sorted ascending -> sort descending (-field)
|
||||
// 3. If sorted descending -> sort ascending (field)
|
||||
if (currentSort === field) {
|
||||
// Currently ascending, switch to descending
|
||||
newSort = '-' + field;
|
||||
} else if (currentSort === '-' + field) {
|
||||
// Currently descending, switch to ascending
|
||||
newSort = field;
|
||||
} else {
|
||||
// Not sorted by this field, start with ascending
|
||||
newSort = field;
|
||||
}
|
||||
|
||||
// Update sort parameter
|
||||
urlParams.set('sort', newSort);
|
||||
|
||||
// Reset to first page when sorting changes
|
||||
urlParams.delete('page');
|
||||
|
||||
// Reload page with new parameters
|
||||
window.location.search = urlParams.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current sort field and direction
|
||||
* @returns {Object} Object with field and direction properties
|
||||
*/
|
||||
function getCurrentSort() {
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const sort = urlParams.get('sort');
|
||||
|
||||
if (!sort) {
|
||||
return { field: null, direction: null };
|
||||
}
|
||||
|
||||
if (sort.startsWith('-')) {
|
||||
return {
|
||||
field: sort.substring(1),
|
||||
direction: 'desc'
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
field: sort,
|
||||
direction: 'asc'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes sort indicators on page load
|
||||
* Adds visual indicators to show current sort state
|
||||
*/
|
||||
function initializeSortIndicators() {
|
||||
const currentSort = getCurrentSort();
|
||||
|
||||
if (!currentSort.field) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find all sort headers and update their indicators
|
||||
const sortHeaders = document.querySelectorAll('[data-sort-field]');
|
||||
sortHeaders.forEach(header => {
|
||||
const field = header.getAttribute('data-sort-field');
|
||||
|
||||
if (field === currentSort.field) {
|
||||
// Add active class or update icon
|
||||
header.classList.add('sort-active');
|
||||
|
||||
// Update icon if present
|
||||
const icon = header.querySelector('.sort-icon');
|
||||
if (icon) {
|
||||
if (currentSort.direction === 'asc') {
|
||||
icon.className = 'bi bi-arrow-up sort-icon';
|
||||
} else {
|
||||
icon.className = 'bi bi-arrow-down sort-icon';
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Initialize sort indicators when DOM is ready
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initializeSortIndicators);
|
||||
} else {
|
||||
initializeSortIndicators();
|
||||
}
|
||||
Reference in New Issue
Block a user