fix: resolve container width issues, spa redirects, and appearance settings overwrite. feat: enhance order/sub details and newsletter layout

This commit is contained in:
Dwindi Ramadhana
2026-02-05 00:09:40 +07:00
parent a0b5f8496d
commit 5f08c18ec7
77 changed files with 7027 additions and 4546 deletions

View File

@@ -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>
))