fix: resolve container width issues, spa redirects, and appearance settings overwrite. feat: enhance order/sub details and newsletter layout
This commit is contained in:
@@ -1,20 +1,20 @@
|
||||
import React, { useState, useCallback } from 'react';
|
||||
import { useQuery, useMutation, useQueryClient, keepPreviousData } from '@tanstack/react-query';
|
||||
import { api } from '@/lib/api';
|
||||
import { Filter, Package, Trash2, RefreshCw } from 'lucide-react';
|
||||
import { Filter, Package, Trash2, RefreshCw, MoreHorizontal, Eye, Edit } from 'lucide-react';
|
||||
import { ErrorCard } from '@/components/ErrorCard';
|
||||
import { getPageLoadErrorMessage } from '@/lib/errorHandling';
|
||||
import { __ } from '@/lib/i18n';
|
||||
import { useFABConfig } from '@/hooks/useFABConfig';
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle
|
||||
} from '@/components/ui/alert-dialog';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Checkbox } from '@/components/ui/checkbox';
|
||||
@@ -27,6 +27,13 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { Link, useNavigate } from 'react-router-dom';
|
||||
import { formatMoney, getStoreCurrency } from '@/lib/currency';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
@@ -45,7 +52,7 @@ function StockBadge({ value, quantity }: { value?: string; quantity?: number })
|
||||
const v = (value || '').toLowerCase();
|
||||
const cls = stockStatusStyle[v] || 'bg-slate-100 text-slate-800';
|
||||
const label = v === 'instock' ? __('In Stock') : v === 'outofstock' ? __('Out of Stock') : v === 'onbackorder' ? __('On Backorder') : v;
|
||||
|
||||
|
||||
return (
|
||||
<span className={`inline-flex items-center px-2 py-0.5 rounded text-xs font-medium ${cls}`}>
|
||||
{label}
|
||||
@@ -62,8 +69,8 @@ export default function Products() {
|
||||
const [type, setType] = useState<string | undefined>(initial.type || undefined);
|
||||
const [stockStatus, setStockStatus] = useState<string | undefined>(initial.stock_status || undefined);
|
||||
const [category, setCategory] = useState<string | undefined>(initial.category || undefined);
|
||||
const [orderby, setOrderby] = useState<'date'|'title'|'id'|'modified'>((initial.orderby as any) || 'date');
|
||||
const [order, setOrder] = useState<'asc'|'desc'>((initial.order as any) || 'desc');
|
||||
const [orderby, setOrderby] = useState<'date' | 'title' | 'id' | 'modified'>((initial.orderby as any) || 'date');
|
||||
const [order, setOrder] = useState<'asc' | 'desc'>((initial.order as any) || 'desc');
|
||||
const [selectedIds, setSelectedIds] = useState<number[]>([]);
|
||||
const [showDeleteDialog, setShowDeleteDialog] = useState(false);
|
||||
const [filterSheetOpen, setFilterSheetOpen] = useState(false);
|
||||
@@ -113,7 +120,7 @@ export default function Products() {
|
||||
const rows = data?.rows;
|
||||
if (!rows) return [];
|
||||
if (!searchQuery.trim()) return rows;
|
||||
|
||||
|
||||
const query = searchQuery.toLowerCase();
|
||||
return rows.filter((product: any) =>
|
||||
product.name?.toLowerCase().includes(query) ||
|
||||
@@ -227,7 +234,7 @@ export default function Products() {
|
||||
<div className="flex flex-col lg:flex-row lg:justify-between lg:items-center gap-3">
|
||||
<div className="flex gap-3">
|
||||
{selectedIds.length > 0 && (
|
||||
<button
|
||||
<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"
|
||||
onClick={handleDeleteClick}
|
||||
disabled={deleteMutation.isPending}
|
||||
@@ -236,8 +243,8 @@ export default function Products() {
|
||||
{__('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}
|
||||
@@ -412,9 +419,37 @@ export default function Products() {
|
||||
</span>
|
||||
</td>
|
||||
<td className="p-3 text-right">
|
||||
<Link to={`/products/${product.id}/edit`} className="text-sm text-primary hover:underline">
|
||||
{__('Edit')}
|
||||
</Link>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="ghost" className="h-8 w-8 p-0">
|
||||
<span className="sr-only">{__('Open menu')}</span>
|
||||
<MoreHorizontal className="h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
<DropdownMenuItem onClick={() => nav(`/products/${product.id}/edit`)}>
|
||||
<Edit className="mr-2 h-4 w-4" />
|
||||
{__('Edit')}
|
||||
</DropdownMenuItem>
|
||||
{product.permalink && (
|
||||
<DropdownMenuItem onClick={() => window.open(product.permalink, '_blank')}>
|
||||
<Eye className="mr-2 h-4 w-4" />
|
||||
{__('View')}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
className="text-destructive focus:text-destructive"
|
||||
onClick={() => {
|
||||
setSelectedIds([product.id]);
|
||||
setShowDeleteDialog(true);
|
||||
}}
|
||||
>
|
||||
<Trash2 className="mr-2 h-4 w-4" />
|
||||
{__('Delete')}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
|
||||
Reference in New Issue
Block a user