feat: Complete toolbar standardization - add refresh button and fix reset filters
**Issue:** - Orders: Missing refresh button (Products had it) - Orders: Reset button had red background style - Products: Reset button had text link style - Inconsistent UX between modules **Solution:** 1. Updated PROJECT_SOP.md with complete toolbar standards 2. Added refresh button to Orders (now mandatory for all CRUD) 3. Standardized reset filters button style (text link) **Changes to PROJECT_SOP.md:** - Added "Refresh (Required)" button type - Added "Reset Filters" button type (text link style) - Updated rules: 11 mandatory rules (was 8) - Rule 2: Refresh button MUST exist in all CRUD lists - Rule 3: Reset filters use text link (NOT button with background) - Updated toolbar layout example with complete structure **Changes to Orders/index.tsx:** - Added refresh button (always visible) - Reset button: bg-red-500/10 text-red-600 → text-muted-foreground hover:text-foreground underline - Reset button text: "Reset" → "Clear filters" - Removed loading indicator (q.isFetching) **Result:** ✅ Both modules now have refresh button ✅ Consistent reset filters style (text link) ✅ Consistent button placement and behavior ✅ Complete toolbar standardization **Standards Now Include:** 1. Delete button (red, conditional) 2. Refresh button (always visible, REQUIRED) 3. Reset filters (text link, conditional) 4. Export/secondary actions (light, optional) Ready for Coupons and Customers CRUD implementation! 🎉
This commit is contained in:
@@ -269,8 +269,9 @@ All CRUD list pages MUST use consistent button styling in the toolbar:
|
|||||||
| Button Type | Classes | Use Case |
|
| Button Type | Classes | Use Case |
|
||||||
|-------------|---------|----------|
|
|-------------|---------|----------|
|
||||||
| **Delete (Destructive)** | `border rounded-md px-3 py-2 text-sm bg-red-600 text-white hover:bg-red-700 disabled:opacity-50 inline-flex items-center gap-2` | Bulk delete action |
|
| **Delete (Destructive)** | `border rounded-md px-3 py-2 text-sm bg-red-600 text-white hover:bg-red-700 disabled:opacity-50 inline-flex items-center gap-2` | Bulk delete action |
|
||||||
| **Refresh** | `border rounded-md px-3 py-2 text-sm hover:bg-accent disabled:opacity-50 inline-flex items-center gap-2` | Refresh data |
|
| **Refresh (Required)** | `border rounded-md px-3 py-2 text-sm hover:bg-accent disabled:opacity-50 inline-flex items-center gap-2` | Refresh data (MUST exist in all CRUD lists) |
|
||||||
| **Export/Secondary** | `border rounded-md px-3 py-2 text-sm hover:bg-accent disabled:opacity-50 inline-flex items-center gap-2` | Secondary actions |
|
| **Reset Filters** | `text-sm text-muted-foreground hover:text-foreground underline` | Clear all active filters |
|
||||||
|
| **Export/Secondary** | `border rounded-md px-3 py-2 text-sm hover:bg-accent disabled:opacity-50 inline-flex items-center gap-2` | Other secondary actions |
|
||||||
|
|
||||||
**Button Structure:**
|
**Button Structure:**
|
||||||
```tsx
|
```tsx
|
||||||
@@ -286,30 +287,49 @@ All CRUD list pages MUST use consistent button styling in the toolbar:
|
|||||||
|
|
||||||
**Rules:**
|
**Rules:**
|
||||||
1. ✅ **Delete button** - Always use `bg-red-600` (NOT `bg-black`)
|
1. ✅ **Delete button** - Always use `bg-red-600` (NOT `bg-black`)
|
||||||
2. ✅ **Icon placement** - Use `inline-flex items-center gap-2` (NOT `inline mr-2`)
|
2. ✅ **Refresh button** - MUST exist in all CRUD list pages (mandatory)
|
||||||
3. ✅ **Destructive actions** - Only show when items selected (conditional render)
|
3. ✅ **Reset filters** - Use text link style (NOT button with background)
|
||||||
4. ✅ **Non-destructive actions** - Can be always visible (use `disabled` state)
|
4. ✅ **Icon placement** - Use `inline-flex items-center gap-2` (NOT `inline mr-2`)
|
||||||
5. ✅ **Consistent spacing** - Use `gap-2` between icon and text
|
5. ✅ **Destructive actions** - Only show when items selected (conditional render)
|
||||||
6. ✅ **Hover states** - Destructive: `hover:bg-red-700`, Secondary: `hover:bg-accent`
|
6. ✅ **Non-destructive actions** - Can be always visible (use `disabled` state)
|
||||||
7. ❌ **Never use `bg-black`** for delete buttons
|
7. ✅ **Consistent spacing** - Use `gap-2` between icon and text
|
||||||
8. ❌ **Never use `inline mr-2`** - use `inline-flex gap-2` instead
|
8. ✅ **Hover states** - Destructive: `hover:bg-red-700`, Secondary: `hover:bg-accent`
|
||||||
|
9. ❌ **Never use `bg-black`** for delete buttons
|
||||||
|
10. ❌ **Never use `inline mr-2`** - use `inline-flex gap-2` instead
|
||||||
|
11. ❌ **Never use button style** for reset filters - use text link
|
||||||
|
|
||||||
**Toolbar Layout:**
|
**Toolbar Layout:**
|
||||||
```tsx
|
```tsx
|
||||||
<div className="flex gap-3">
|
<div className="flex flex-col lg:flex-row lg:justify-between lg:items-center gap-3">
|
||||||
{/* Bulk Actions - Show only when items selected */}
|
{/* Left: Bulk Actions */}
|
||||||
{selectedIds.length > 0 && (
|
<div className="flex gap-3">
|
||||||
<button className="...bg-red-600...">
|
{/* Delete - Show only when items selected */}
|
||||||
<Trash2 className="w-4 h-4" />
|
{selectedIds.length > 0 && (
|
||||||
{__('Delete')} ({selectedIds.length})
|
<button className="border rounded-md px-3 py-2 text-sm bg-red-600 text-white hover:bg-red-700 disabled:opacity-50 inline-flex items-center gap-2">
|
||||||
</button>
|
<Trash2 className="w-4 h-4" />
|
||||||
)}
|
{__('Delete')} ({selectedIds.length})
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Always-visible actions */}
|
{/* Refresh - Always visible (REQUIRED) */}
|
||||||
<button className="...hover:bg-accent...">
|
<button className="border rounded-md px-3 py-2 text-sm hover:bg-accent disabled:opacity-50 inline-flex items-center gap-2">
|
||||||
<RefreshCw className="w-4 h-4" />
|
<RefreshCw className="w-4 h-4" />
|
||||||
{__('Refresh')}
|
{__('Refresh')}
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right: Filters */}
|
||||||
|
<div className="flex gap-3 flex-wrap items-center">
|
||||||
|
<Select>...</Select>
|
||||||
|
<Select>...</Select>
|
||||||
|
|
||||||
|
{/* Reset Filters - Text link style */}
|
||||||
|
{activeFiltersCount > 0 && (
|
||||||
|
<button className="text-sm text-muted-foreground hover:text-foreground underline">
|
||||||
|
{__('Clear filters')}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -255,6 +255,15 @@ export default function Orders() {
|
|||||||
{__('Delete')} ({selectedIds.length})
|
{__('Delete')} ({selectedIds.length})
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<button
|
||||||
|
className="border rounded-md px-3 py-2 text-sm hover:bg-accent disabled:opacity-50 inline-flex items-center gap-2"
|
||||||
|
onClick={handleRefresh}
|
||||||
|
disabled={q.isLoading || isRefreshing}
|
||||||
|
>
|
||||||
|
<RefreshCw className={`w-4 h-4 ${isRefreshing ? 'animate-spin' : ''}`} />
|
||||||
|
{__('Refresh')}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex gap-2 items-center">
|
<div className="flex gap-2 items-center">
|
||||||
@@ -299,13 +308,12 @@ export default function Orders() {
|
|||||||
|
|
||||||
{activeFiltersCount > 0 && (
|
{activeFiltersCount > 0 && (
|
||||||
<button
|
<button
|
||||||
className="rounded-md px-3 py-2 text-sm bg-red-500/10 text-red-600"
|
className="text-sm text-muted-foreground hover:text-foreground underline"
|
||||||
onClick={handleResetFilters}
|
onClick={handleResetFilters}
|
||||||
>
|
>
|
||||||
{__('Reset')}
|
{__('Clear filters')}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{q.isFetching && <span className="text-sm opacity-70">{__('Loading…')}</span>}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user