Improve browser detection and table UX for Firefox
All checks were successful
Build Frontend / build (push) Successful in 1m10s

Standardizes browser detection logic across admin and storefront pages to more accurately identify Firefox. Updates table rendering logic to provide better compatibility and fallback for Firefox, including conditional use of AnimatePresence and improved loading/empty states. Refines table UI styles for consistency and accessibility.
This commit is contained in:
g
2026-01-12 08:59:04 +00:00
parent 064cd7a486
commit 3f9d28bf1b
7 changed files with 400 additions and 114 deletions

View File

@@ -158,10 +158,12 @@ export default function OrderTable() {
// Fetch orders with server-side pagination
// State for browser detection
// Browser detection
const [isFirefox, setIsFirefox] = useState(false);
useEffect(() => {
setIsFirefox(navigator.userAgent.toLowerCase().indexOf("firefox") > -1);
const ua = navigator.userAgent.toLowerCase();
setIsFirefox(ua.includes("firefox") && !ua.includes("chrome"));
}, []);
const fetchOrders = useCallback(async () => {
@@ -401,9 +403,9 @@ export default function OrderTable() {
return (
<div className="space-y-4">
<Card className="border-white/10 bg-black/40 backdrop-blur-xl shadow-2xl overflow-hidden rounded-xl">
<Card className="border-border/40 bg-background/50 backdrop-blur-sm shadow-sm overflow-hidden">
{/* Filters header */}
<div className="p-4 border-b border-white/5 bg-white/[0.02]">
<div className="p-4 border-b border-border/50 bg-muted/30">
<div className="flex flex-col lg:flex-row justify-between items-start lg:items-center gap-4">
<div className="flex flex-col sm:flex-row gap-2 sm:items-center w-full lg:w-auto">
<StatusFilter
@@ -423,7 +425,7 @@ export default function OrderTable() {
disabled={exporting}
variant="outline"
size="sm"
className="bg-black/20 border-white/10 hover:bg-white/5 hover:text-white transition-colors"
className="bg-background/50 border-border/50 hover:bg-muted/50 transition-colors"
>
{exporting ? (
<>
@@ -443,7 +445,7 @@ export default function OrderTable() {
<div className="flex items-center gap-2 self-end lg:self-auto">
<AlertDialog>
<AlertDialogTrigger asChild>
<Button disabled={selectedOrders.size === 0 || isShipping} className="shadow-lg bg-indigo-600 hover:bg-indigo-700 text-white border-0 transition-all hover:scale-105 active:scale-95">
<Button disabled={selectedOrders.size === 0 || isShipping} className="shadow-md">
<Truck className="mr-2 h-4 w-4" />
{isShipping ? (
<Loader2 className="h-4 w-4 animate-spin" />
@@ -484,8 +486,8 @@ export default function OrderTable() {
)}
<div className="max-h-[calc(100vh-350px)] overflow-auto">
<Table>
<TableHeader className="bg-white/[0.02] sticky top-0 z-20 backdrop-blur-md">
<TableRow className="hover:bg-transparent border-white/5">
<TableHeader className="bg-muted/30 sticky top-0 z-20">
<TableRow className="hover:bg-transparent border-border/50">
<TableHead className="w-12">
<Checkbox
checked={selectedOrders.size === paginatedOrders.length && paginatedOrders.length > 0}
@@ -525,7 +527,7 @@ export default function OrderTable() {
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ duration: 0.2 }}
className="group hover:bg-white/[0.03] border-b border-white/5 transition-colors"
className="group hover:bg-muted/50 border-b border-border/50 transition-colors"
>
<TableCell>
<Checkbox
@@ -641,11 +643,11 @@ export default function OrderTable() {
<motion.tr
key={order._id}
layout
initial={{ opacity: 0, scale: 0.98 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.98 }}
transition={{ duration: 0.2 }}
className="group hover:bg-white/[0.03] border-b border-white/5 transition-colors"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2, delay: index * 0.03 }}
className="group hover:bg-muted/50 border-b border-border/50 transition-colors"
>
<TableCell>
<Checkbox

View File

@@ -18,6 +18,7 @@ import {
Archive
} from "lucide-react";
import { Button } from "@/components/ui/button";
import React, { useState, useEffect } from "react";
import { Product } from "@/models/products";
import { Badge } from "@/components/ui/badge";
import { Switch } from "@/components/ui/switch";
@@ -191,6 +192,15 @@ const ProductTable = ({
</motion.tr>
);
const [searchQuery, setSearchQuery] = useState("");
// Browser detection
const [isFirefox, setIsFirefox] = useState(false);
useEffect(() => {
const ua = navigator.userAgent.toLowerCase();
setIsFirefox(ua.includes("firefox") && !ua.includes("chrome"));
}, []);
const renderTableHeader = () => (
<TableHeader className="bg-muted/50 sticky top-0 z-10">
<TableRow className="hover:bg-transparent border-border/50">
@@ -228,8 +238,8 @@ const ProductTable = ({
<Table>
{renderTableHeader()}
<TableBody>
<AnimatePresence>
{loading ? (
{isFirefox ? (
loading ? (
<TableRow>
<TableCell colSpan={6} className="h-32 text-center text-muted-foreground">
<div className="flex flex-col items-center gap-2">
@@ -249,8 +259,32 @@ const ProductTable = ({
</div>
</TableCell>
</TableRow>
)}
</AnimatePresence>
)
) : (
<AnimatePresence>
{loading ? (
<TableRow>
<TableCell colSpan={6} className="h-32 text-center text-muted-foreground">
<div className="flex flex-col items-center gap-2">
<div className="h-6 w-6 animate-spin rounded-full border-2 border-indigo-500 border-t-transparent" />
<span>Loading products...</span>
</div>
</TableCell>
</TableRow>
) : sortedEnabledProducts.length > 0 ? (
sortedEnabledProducts.map((product, index) => renderProductRow(product, index))
) : (
<TableRow>
<TableCell colSpan={6} className="h-32 text-center text-muted-foreground">
<div className="flex flex-col items-center justify-center gap-2">
<PackageX className="h-8 w-8 opacity-20" />
<p>No active products found</p>
</div>
</TableCell>
</TableRow>
)}
</AnimatePresence>
)}
</TableBody>
</Table>
</div>

View File

@@ -1,3 +1,4 @@
import React, { useState, useEffect } from "react";
import { Skeleton } from "@/components/ui/skeleton";
import {
Table,
@@ -26,6 +27,14 @@ export const ShippingTable: React.FC<ShippingTableProps> = ({
onEditShipping,
onDeleteShipping,
}) => {
// Browser detection
const [isFirefox, setIsFirefox] = useState(false);
useEffect(() => {
const ua = navigator.userAgent.toLowerCase();
setIsFirefox(ua.includes("firefox") && !ua.includes("chrome"));
}, []);
return (
<Card className="border-border/40 bg-background/50 backdrop-blur-sm shadow-sm overflow-hidden">
<CardHeader className="py-4 px-6 border-b border-border/50 bg-muted/30">
@@ -45,8 +54,8 @@ export const ShippingTable: React.FC<ShippingTableProps> = ({
</TableRow>
</TableHeader>
<TableBody>
<AnimatePresence mode="popLayout">
{loading ? (
{isFirefox ? (
loading ? (
<TableRow>
<TableCell colSpan={3} className="h-24 text-center">
<div className="flex items-center justify-center gap-2 text-muted-foreground">
@@ -61,7 +70,6 @@ export const ShippingTable: React.FC<ShippingTableProps> = ({
key={method._id}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2, delay: index * 0.05 }}
className="group hover:bg-muted/40 border-b border-border/50 transition-colors"
>
@@ -100,13 +108,76 @@ export const ShippingTable: React.FC<ShippingTableProps> = ({
<TableRow>
<TableCell colSpan={3} className="h-32 text-center text-muted-foreground">
<div className="flex flex-col items-center justify-center gap-2">
<PackageX className="h-8 w-8 opacity-50" />
<PackageX className="h-8 w-8 opacity-20" />
<p>No shipping methods found</p>
</div>
</TableCell>
</TableRow>
)}
</AnimatePresence>
)
) : (
<AnimatePresence mode="popLayout">
{loading ? (
<TableRow>
<TableCell colSpan={3} className="h-24 text-center">
<div className="flex items-center justify-center gap-2 text-muted-foreground">
<Skeleton className="h-4 w-4 rounded-full" />
Loading methods...
</div>
</TableCell>
</TableRow>
) : shippingMethods.length > 0 ? (
shippingMethods.map((method, index) => (
<motion.tr
key={method._id}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0 }}
transition={{ duration: 0.2, delay: index * 0.05 }}
className="group hover:bg-muted/40 border-b border-border/50 transition-colors"
>
<TableCell className="font-medium pl-6">
<div className="flex items-center gap-3">
<div className="h-8 w-8 rounded bg-primary/10 flex items-center justify-center">
<Truck className="h-4 w-4 text-primary" />
</div>
{method.name}
</div>
</TableCell>
<TableCell className="text-center font-mono">£{method.price}</TableCell>
<TableCell className="text-right pr-6">
<div className="flex justify-end gap-1">
<Button
size="icon"
variant="ghost"
className="h-8 w-8 text-muted-foreground hover:text-primary hover:bg-primary/10 transition-colors"
onClick={() => onEditShipping(method)}
>
<Edit className="h-4 w-4" />
</Button>
<Button
size="icon"
variant="ghost"
className="h-8 w-8 text-muted-foreground hover:text-destructive hover:bg-destructive/10 transition-colors"
onClick={() => onDeleteShipping(method._id ?? "")}
>
<Trash className="h-4 w-4" />
</Button>
</div>
</TableCell>
</motion.tr>
))
) : (
<TableRow>
<TableCell colSpan={3} className="h-32 text-center text-muted-foreground">
<div className="flex flex-col items-center justify-center gap-2">
<PackageX className="h-8 w-8 opacity-20" />
<p>No shipping methods found</p>
</div>
</TableCell>
</TableRow>
)}
</AnimatePresence>
)}
</TableBody>
</Table>
</div>