fix: implement handleSort method for sortable table headers
- Update th_sortable macro to call handleSort(key) instead of inline logic - Add handleSort method to subscriptions.js, subscription-tiers.js, billing-history.js - Add sort_by and sort_order params to API calls in all three files - Reset to page 1 when sort changes 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -77,8 +77,8 @@
|
|||||||
Parameters:
|
Parameters:
|
||||||
- key: The data key/column name for sorting
|
- key: The data key/column name for sorting
|
||||||
- label: Display label for the column
|
- label: Display label for the column
|
||||||
- sort_func: Alpine.js function name to call on click (default: 'sortBy')
|
- sort_key_var: Alpine.js variable for current sort key (default: 'sortBy')
|
||||||
- sort_order_var: Alpine.js variable for current sort order (default: 'sortOrder')
|
- sort_order_var: Alpine.js variable for sort order (default: 'sortOrder')
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
{% call table_header_custom() %}
|
{% call table_header_custom() %}
|
||||||
@@ -97,27 +97,27 @@
|
|||||||
this.sortBy = key;
|
this.sortBy = key;
|
||||||
this.sortOrder = 'asc';
|
this.sortOrder = 'asc';
|
||||||
}
|
}
|
||||||
this.loadData();
|
this.loadData(); // or loadSubscriptions(), etc.
|
||||||
}
|
}
|
||||||
#}
|
#}
|
||||||
{% macro th_sortable(key, label, sort_func='sortBy', sort_order_var='sortOrder') %}
|
{% macro th_sortable(key, label, sort_key_var='sortBy', sort_order_var='sortOrder') %}
|
||||||
<th class="px-4 py-3">
|
<th class="px-4 py-3">
|
||||||
<button
|
<button
|
||||||
@click="{{ sort_func }} = '{{ key }}'; {{ sort_order_var }} = {{ sort_func }} === '{{ key }}' && {{ sort_order_var }} === 'asc' ? 'desc' : 'asc'; loadData()"
|
@click="handleSort('{{ key }}')"
|
||||||
class="flex items-center space-x-1 hover:text-gray-700 dark:hover:text-gray-200 transition-colors group"
|
class="flex items-center space-x-1 hover:text-gray-700 dark:hover:text-gray-200 transition-colors group"
|
||||||
>
|
>
|
||||||
<span>{{ label }}</span>
|
<span>{{ label }}</span>
|
||||||
<span class="flex flex-col">
|
<span class="flex flex-col">
|
||||||
<svg
|
<svg
|
||||||
class="w-3 h-3 -mb-1 transition-colors"
|
class="w-3 h-3 -mb-1 transition-colors"
|
||||||
:class="{{ sort_func }} === '{{ key }}' && {{ sort_order_var }} === 'asc' ? 'text-purple-600 dark:text-purple-400' : 'text-gray-300 dark:text-gray-600 group-hover:text-gray-400'"
|
:class="{{ sort_key_var }} === '{{ key }}' && {{ sort_order_var }} === 'asc' ? 'text-purple-600 dark:text-purple-400' : 'text-gray-300 dark:text-gray-600 group-hover:text-gray-400'"
|
||||||
fill="currentColor" viewBox="0 0 20 20"
|
fill="currentColor" viewBox="0 0 20 20"
|
||||||
>
|
>
|
||||||
<path d="M5 12l5-5 5 5H5z"/>
|
<path d="M5 12l5-5 5 5H5z"/>
|
||||||
</svg>
|
</svg>
|
||||||
<svg
|
<svg
|
||||||
class="w-3 h-3 -mt-1 transition-colors"
|
class="w-3 h-3 -mt-1 transition-colors"
|
||||||
:class="{{ sort_func }} === '{{ key }}' && {{ sort_order_var }} === 'desc' ? 'text-purple-600 dark:text-purple-400' : 'text-gray-300 dark:text-gray-600 group-hover:text-gray-400'"
|
:class="{{ sort_key_var }} === '{{ key }}' && {{ sort_order_var }} === 'desc' ? 'text-purple-600 dark:text-purple-400' : 'text-gray-300 dark:text-gray-600 group-hover:text-gray-400'"
|
||||||
fill="currentColor" viewBox="0 0 20 20"
|
fill="currentColor" viewBox="0 0 20 20"
|
||||||
>
|
>
|
||||||
<path d="M15 8l-5 5-5-5h10z"/>
|
<path d="M15 8l-5 5-5-5h10z"/>
|
||||||
|
|||||||
@@ -136,6 +136,8 @@ function adminBillingHistory() {
|
|||||||
params.append('per_page', this.pagination.per_page);
|
params.append('per_page', this.pagination.per_page);
|
||||||
if (this.filters.vendor_id) params.append('vendor_id', this.filters.vendor_id);
|
if (this.filters.vendor_id) params.append('vendor_id', this.filters.vendor_id);
|
||||||
if (this.filters.status) params.append('status', this.filters.status);
|
if (this.filters.status) params.append('status', this.filters.status);
|
||||||
|
if (this.sortBy) params.append('sort_by', this.sortBy);
|
||||||
|
if (this.sortOrder) params.append('sort_order', this.sortOrder);
|
||||||
|
|
||||||
const data = await apiClient.get(`/admin/subscriptions/billing/history?${params}`);
|
const data = await apiClient.get(`/admin/subscriptions/billing/history?${params}`);
|
||||||
this.invoices = data.invoices || [];
|
this.invoices = data.invoices || [];
|
||||||
@@ -154,6 +156,17 @@ function adminBillingHistory() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleSort(key) {
|
||||||
|
if (this.sortBy === key) {
|
||||||
|
this.sortOrder = this.sortOrder === 'asc' ? 'desc' : 'asc';
|
||||||
|
} else {
|
||||||
|
this.sortBy = key;
|
||||||
|
this.sortOrder = 'asc';
|
||||||
|
}
|
||||||
|
this.pagination.page = 1;
|
||||||
|
this.loadInvoices();
|
||||||
|
},
|
||||||
|
|
||||||
updateStatusCounts() {
|
updateStatusCounts() {
|
||||||
// Reset counts
|
// Reset counts
|
||||||
this.statusCounts = {
|
this.statusCounts = {
|
||||||
|
|||||||
@@ -74,6 +74,8 @@ function adminSubscriptionTiers() {
|
|||||||
try {
|
try {
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
params.append('include_inactive', this.includeInactive);
|
params.append('include_inactive', this.includeInactive);
|
||||||
|
if (this.sortBy) params.append('sort_by', this.sortBy);
|
||||||
|
if (this.sortOrder) params.append('sort_order', this.sortOrder);
|
||||||
|
|
||||||
const data = await apiClient.get(`/admin/subscriptions/tiers?${params}`);
|
const data = await apiClient.get(`/admin/subscriptions/tiers?${params}`);
|
||||||
this.tiers = data.tiers || [];
|
this.tiers = data.tiers || [];
|
||||||
@@ -86,6 +88,16 @@ function adminSubscriptionTiers() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleSort(key) {
|
||||||
|
if (this.sortBy === key) {
|
||||||
|
this.sortOrder = this.sortOrder === 'asc' ? 'desc' : 'asc';
|
||||||
|
} else {
|
||||||
|
this.sortBy = key;
|
||||||
|
this.sortOrder = 'asc';
|
||||||
|
}
|
||||||
|
this.loadTiers();
|
||||||
|
},
|
||||||
|
|
||||||
async loadStats() {
|
async loadStats() {
|
||||||
try {
|
try {
|
||||||
const data = await apiClient.get('/admin/subscriptions/stats');
|
const data = await apiClient.get('/admin/subscriptions/stats');
|
||||||
|
|||||||
@@ -144,6 +144,8 @@ function adminSubscriptions() {
|
|||||||
if (this.filters.status) params.append('status', this.filters.status);
|
if (this.filters.status) params.append('status', this.filters.status);
|
||||||
if (this.filters.tier) params.append('tier', this.filters.tier);
|
if (this.filters.tier) params.append('tier', this.filters.tier);
|
||||||
if (this.filters.search) params.append('search', this.filters.search);
|
if (this.filters.search) params.append('search', this.filters.search);
|
||||||
|
if (this.sortBy) params.append('sort_by', this.sortBy);
|
||||||
|
if (this.sortOrder) params.append('sort_order', this.sortOrder);
|
||||||
|
|
||||||
const data = await apiClient.get(`/admin/subscriptions?${params}`);
|
const data = await apiClient.get(`/admin/subscriptions?${params}`);
|
||||||
this.subscriptions = data.subscriptions || [];
|
this.subscriptions = data.subscriptions || [];
|
||||||
@@ -168,6 +170,17 @@ function adminSubscriptions() {
|
|||||||
this.loadSubscriptions();
|
this.loadSubscriptions();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleSort(key) {
|
||||||
|
if (this.sortBy === key) {
|
||||||
|
this.sortOrder = this.sortOrder === 'asc' ? 'desc' : 'asc';
|
||||||
|
} else {
|
||||||
|
this.sortBy = key;
|
||||||
|
this.sortOrder = 'asc';
|
||||||
|
}
|
||||||
|
this.pagination.page = 1;
|
||||||
|
this.loadSubscriptions();
|
||||||
|
},
|
||||||
|
|
||||||
previousPage() {
|
previousPage() {
|
||||||
if (this.pagination.page > 1) {
|
if (this.pagination.page > 1) {
|
||||||
this.pagination.page--;
|
this.pagination.page--;
|
||||||
|
|||||||
Reference in New Issue
Block a user