From 211cdc71f95d50565176a8fe94f703d65da75a02 Mon Sep 17 00:00:00 2001 From: g Date: Mon, 12 Jan 2026 06:53:28 +0000 Subject: [PATCH] Enhance dashboard UI and add order timeline Refactored dashboard pages for improved layout and visual consistency using Card components, motion animations, and updated color schemes. Added an OrderTimeline component to the order details page to visualize order lifecycle. Improved customer management page with better sorting, searching, and a detailed customer dialog. Updated storefront settings page with a modernized layout and clearer sectioning. --- app/dashboard/orders/[id]/page.tsx | 187 +++-- app/dashboard/storefront/customers/page.tsx | 823 ++++++++++---------- app/dashboard/storefront/page.tsx | 493 ++++++------ components/dashboard/ChatDetail.tsx | 277 ++++--- components/dashboard/ChatTable.tsx | 405 +++++----- components/dashboard/content.tsx | 248 +++--- components/dashboard/order-stats.tsx | 34 +- components/dashboard/quick-actions.tsx | 75 ++ components/dashboard/recent-activity.tsx | 119 +++ components/orders/order-timeline.tsx | 86 ++ components/tables/order-table.tsx | 373 ++++----- public/git-info.json | 4 +- 12 files changed, 1793 insertions(+), 1331 deletions(-) create mode 100644 components/dashboard/quick-actions.tsx create mode 100644 components/dashboard/recent-activity.tsx create mode 100644 components/orders/order-timeline.tsx diff --git a/app/dashboard/orders/[id]/page.tsx b/app/dashboard/orders/[id]/page.tsx index 2b4cb70..2ebd044 100644 --- a/app/dashboard/orders/[id]/page.tsx +++ b/app/dashboard/orders/[id]/page.tsx @@ -39,6 +39,8 @@ import { } from "@/components/ui/alert-dialog"; import Layout from "@/components/layout/layout"; import { cacheUtils } from '@/lib/api-client'; +import OrderTimeline from "@/components/orders/order-timeline"; +import { motion, AnimatePresence } from "framer-motion"; interface Order { orderId: string; @@ -170,7 +172,7 @@ export default function OrderDetailsPage() { authToken: string ): Promise> => { const productNamesMap: Record = {}; - + // Process each product ID independently const fetchPromises = productIds.map(async (id) => { try { @@ -184,10 +186,10 @@ export default function OrderDetailsPage() { productNamesMap[id] = "Unknown Product (Deleted)"; } }); - + // Wait for all fetch operations to complete (successful or failed) await Promise.all(fetchPromises); - + return productNamesMap; }; @@ -195,38 +197,38 @@ export default function OrderDetailsPage() { try { // Add a loading state to give feedback const loadingToast = toast.loading("Marking order as paid..."); - + // Log the request for debugging console.log(`Sending request to /orders/${orderId}/status with clientFetch`); console.log("Request payload:", { status: "paid" }); - + // Use clientFetch which handles API URL and auth token automatically const response = await clientFetch(`/orders/${orderId}/status`, { method: "PUT", body: JSON.stringify({ status: "paid" }), }); - + // Log the response console.log("API response:", response); toast.dismiss(loadingToast); - + if (response && response.message === "Order status updated successfully") { // Update both states setIsPaid(true); - setOrder((prevOrder) => (prevOrder ? { - ...prevOrder, + setOrder((prevOrder) => (prevOrder ? { + ...prevOrder, status: "paid", // Clear underpayment flags when marking as paid underpaid: false, underpaymentAmount: 0 } : null)); - + // Invalidate order cache to ensure fresh data everywhere cacheUtils.invalidateOrderData(orderId as string); - + toast.success("Order marked as paid successfully"); - + // Refresh order data to get latest status setTimeout(() => { setRefreshTrigger(prev => prev + 1); @@ -240,14 +242,14 @@ export default function OrderDetailsPage() { } } catch (error: any) { console.error("Failed to mark order as paid:", error); - + // More detailed error handling let errorMessage = "Failed to mark order as paid"; - + if (error.message) { errorMessage += `: ${error.message}`; } - + if (error.response) { try { const errorData = await error.response.json(); @@ -259,7 +261,7 @@ export default function OrderDetailsPage() { console.error("Could not parse error response:", e); } } - + toast.error(errorMessage); } }; @@ -317,7 +319,7 @@ export default function OrderDetailsPage() { ...prevOrder, trackingNumber: trackingNumber } : null); - + toast.success("Tracking number added successfully!"); } catch (err: any) { console.error("Failed to add tracking number:", err); @@ -330,7 +332,7 @@ export default function OrderDetailsPage() { const handleMarkAsAcknowledged = async () => { try { setIsAcknowledging(true); - + // Use clientFetch which handles API URL and auth token automatically const response = await clientFetch(`/orders/${orderId}/status`, { method: "PUT", @@ -382,7 +384,7 @@ export default function OrderDetailsPage() { const handleCancelOrder = async () => { try { setIsCancelling(true); - + // Use clientFetch which handles API URL and auth token automatically const response = await clientFetch(`/orders/${orderId}/status`, { method: "PUT", @@ -429,10 +431,10 @@ export default function OrderDetailsPage() { const productIds = data.order.products.map((product) => product.productId); const productNamesMap = await fetchProductNames(productIds, authToken); setProductNames(productNamesMap); - + setTimeout(() => { setProductNames(prev => { - const newMap = {...prev}; + const newMap = { ...prev }; productIds.forEach(id => { if (!newMap[id] || newMap[id] === "Loading...") { newMap[id] = "Unknown Product (Deleted)"; @@ -440,7 +442,7 @@ export default function OrderDetailsPage() { }); return newMap; }); - }, 3000); + }, 3000); if (data.order.status === "paid") { setIsPaid(true); @@ -460,7 +462,7 @@ export default function OrderDetailsPage() { const fetchAdjacentOrders = async () => { try { const authToken = document.cookie.split("Authorization=")[1]; - + if (!order?.orderId) return; // Get the current numerical orderId @@ -470,7 +472,7 @@ export default function OrderDetailsPage() { // Use the new optimized backend endpoint to get adjacent orders const adjacentOrdersUrl = `${process.env.NEXT_PUBLIC_API_URL}/orders/adjacent/${currentOrderId}`; console.log('Fetching adjacent orders:', adjacentOrdersUrl); - + const adjacentOrdersRes = await fetchData( adjacentOrdersUrl, { @@ -488,17 +490,17 @@ export default function OrderDetailsPage() { // Set the next and previous order IDs const { newer, older } = adjacentOrdersRes; - + // Set IDs for navigation setPrevOrderId(newer?._id || null); setNextOrderId(older?._id || null); - + if (newer) { console.log(`Newer order: ${newer.orderId} (ID: ${newer._id})`); } else { console.log('No newer order found'); } - + if (older) { console.log(`Older order: ${older.orderId} (ID: ${older._id})`); } else { @@ -544,7 +546,7 @@ export default function OrderDetailsPage() { ...prevOrder, trackingNumber: trackingNumber } : null); - + toast.success("Tracking number updated successfully!"); setTrackingNumber(""); // Clear the input } catch (err: any) { @@ -569,11 +571,11 @@ export default function OrderDetailsPage() { try { const lines = []; - + // Order number lines.push(`Order Number: ${order.orderId}`); lines.push(''); - + // Order details lines.push('Order Details:'); if (order.products && order.products.length > 0) { @@ -582,30 +584,30 @@ export default function OrderDetailsPage() { lines.push(` - ${productName} (Qty: ${product.quantity} @ £${product.pricePerUnit.toFixed(2)} = £${product.totalItemPrice.toFixed(2)})`); }); } - + // Shipping if (order.shippingMethod) { lines.push(` - Shipping: ${order.shippingMethod.type} (£${order.shippingMethod.price.toFixed(2)})`); } - + // Discount if (order.discountAmount && order.discountAmount > 0) { lines.push(` - Discount: -£${order.discountAmount.toFixed(2)}${order.promotionCode ? ` (Promo: ${order.promotionCode})` : ''}`); } - + // Subtotal if different from total if (order.subtotalBeforeDiscount && order.subtotalBeforeDiscount !== order.totalPrice) { lines.push(` - Subtotal: £${order.subtotalBeforeDiscount.toFixed(2)}`); } - + // Total lines.push(` - Total: £${order.totalPrice.toFixed(2)}`); lines.push(''); - + // Address lines.push('Address:'); lines.push(order.pgpAddress || 'N/A'); - + const textToCopy = lines.join('\n'); await navigator.clipboard.writeText(textToCopy); toast.success("Order data copied to clipboard!"); @@ -618,28 +620,28 @@ export default function OrderDetailsPage() { // Helper function to check if order is underpaid const isOrderUnderpaid = (order: Order | null) => { // More robust check - only show underpaid if status is NOT paid and underpayment exists - return order?.underpaid === true && - order?.underpaymentAmount && - order.underpaymentAmount > 0 && - order.status !== "paid" && - order.status !== "completed" && - order.status !== "shipped"; + return order?.underpaid === true && + order?.underpaymentAmount && + order.underpaymentAmount > 0 && + order.status !== "paid" && + order.status !== "completed" && + order.status !== "shipped"; }; // Helper function to get underpaid information const getUnderpaidInfo = (order: Order | null) => { if (!isOrderUnderpaid(order)) return null; - + const received = order?.lastBalanceReceived || 0; const required = order?.cryptoTotal || 0; const missing = order?.underpaymentAmount || 0; - + // Calculate LTC to GBP exchange rate from order data const ltcToGbpRate = required > 0 ? (order?.totalPrice || 0) / required : 0; const receivedGbp = received * ltcToGbpRate; const requiredGbp = order?.totalPrice || 0; const missingGbp = missing * ltcToGbpRate; - + return { received, required, @@ -772,7 +774,7 @@ export default function OrderDetailsPage() {

{underpaidInfo.percentage}% paid

- + {order?.paymentAddress && (

Payment Address:

@@ -791,10 +793,26 @@ export default function OrderDetailsPage() { )} + {/* Order Timeline */} + + + Order Lifecycle + + + + + - - -
+ {/* Left Column - Order Details */}
{/* Products Card */} @@ -929,7 +947,7 @@ export default function OrderDetailsPage() {
Customer Since - {customerInsights.firstOrder ? + {customerInsights.firstOrder ? new Date(customerInsights.firstOrder).toLocaleDateString('en-GB', { day: '2-digit', month: 'short', @@ -1092,34 +1110,34 @@ export default function OrderDetailsPage() { )} {/* Cancel Order Button */} - {order?.status !== "cancelled" && - order?.status !== "completed" && - order?.status !== "shipped" && ( - - - - - - - Cancel Order - - Are you sure you want to cancel this order? This action cannot be undone. - - - - Cancel - - Confirm Cancel - - - - - )} + {order?.status !== "cancelled" && + order?.status !== "completed" && + order?.status !== "shipped" && ( + + + + + + + Cancel Order + + Are you sure you want to cancel this order? This action cannot be undone. + + + + Cancel + + Confirm Cancel + + + + + )} {/* No Actions Available Message */} {(order?.status === "completed" || order?.status === "cancelled") && ( @@ -1168,11 +1186,10 @@ export default function OrderDetailsPage() { {[...Array(5)].map((_, i) => ( @@ -1198,10 +1215,10 @@ export default function OrderDetailsPage() { )}
-
- - {/* Shipping Dialog removed; use inline tracking input above */} +
+ + {/* Shipping Dialog removed; use inline tracking input above */} ); -} \ No newline at end of file +} diff --git a/app/dashboard/storefront/customers/page.tsx b/app/dashboard/storefront/customers/page.tsx index 9841853..f4099c3 100644 --- a/app/dashboard/storefront/customers/page.tsx +++ b/app/dashboard/storefront/customers/page.tsx @@ -30,27 +30,32 @@ import { DialogFooter, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; -import { - ChevronLeft, - ChevronRight, - Loader2, - Users, +import { + ChevronLeft, + ChevronRight, + Loader2, + Users, ArrowUpDown, MessageCircle, UserPlus, MoreHorizontal, Search, - X + X, + CreditCard, + Calendar, + ShoppingBag } from "lucide-react"; import { Badge } from "@/components/ui/badge"; import { Input } from "@/components/ui/input"; import { Skeleton } from "@/components/ui/skeleton"; +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; +import { motion, AnimatePresence } from "framer-motion"; import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, -} from "@/components/ui/dropdown-menu"; +} from "@/components/ui/dropdown-menu"; export default function CustomerManagementPage() { const router = useRouter(); @@ -71,32 +76,32 @@ export default function CustomerManagementPage() { try { setLoading(true); const response = await getCustomers(page, itemsPerPage); - + // Sort customers based on current sort config let sortedCustomers = [...response.customers]; sortedCustomers.sort((a, b) => { if (sortConfig.column === "totalOrders") { - return sortConfig.direction === "asc" - ? a.totalOrders - b.totalOrders + return sortConfig.direction === "asc" + ? a.totalOrders - b.totalOrders : b.totalOrders - a.totalOrders; } else if (sortConfig.column === "totalSpent") { - return sortConfig.direction === "asc" - ? a.totalSpent - b.totalSpent + return sortConfig.direction === "asc" + ? a.totalSpent - b.totalSpent : b.totalSpent - a.totalSpent; } else if (sortConfig.column === "lastOrderDate") { // Handle null lastOrderDate values if (!a.lastOrderDate && !b.lastOrderDate) return 0; if (!a.lastOrderDate) return sortConfig.direction === "asc" ? -1 : 1; if (!b.lastOrderDate) return sortConfig.direction === "asc" ? 1 : -1; - + // Both have valid dates - return sortConfig.direction === "asc" - ? new Date(a.lastOrderDate).getTime() - new Date(b.lastOrderDate).getTime() + return sortConfig.direction === "asc" + ? new Date(a.lastOrderDate).getTime() - new Date(b.lastOrderDate).getTime() : new Date(b.lastOrderDate).getTime() - new Date(a.lastOrderDate).getTime(); } return 0; }); - + setCustomers(sortedCustomers); setFilteredCustomers(sortedCustomers); setTotalPages(Math.ceil(response.total / itemsPerPage)); @@ -138,424 +143,444 @@ export default function CustomerManagementPage() { } }, [searchQuery, customers]); - const handlePageChange = (newPage: number) => { - setPage(newPage); - }; + const handlePageChange = (newPage: number) => { + setPage(newPage); + }; - const handleItemsPerPageChange = (value: string) => { - setItemsPerPage(parseInt(value, 10)); - setPage(1); - }; + const handleItemsPerPageChange = (value: string) => { + setItemsPerPage(parseInt(value, 10)); + setPage(1); + }; - const handleSort = (column: "totalOrders" | "totalSpent" | "lastOrderDate") => { - setSortConfig(prev => ({ - column, - direction: prev.column === column && prev.direction === "asc" ? "desc" : "asc" - })); - }; + const handleSort = (column: "totalOrders" | "totalSpent" | "lastOrderDate") => { + setSortConfig(prev => ({ + column, + direction: prev.column === column && prev.direction === "asc" ? "desc" : "asc" + })); + }; - const clearSearch = () => { - setSearchQuery(""); - }; + const clearSearch = () => { + setSearchQuery(""); + }; - const formatDate = (dateString: string | null | undefined) => { - if (!dateString) return "N/A"; - try { - const date = new Date(dateString); - return new Intl.DateTimeFormat('en-GB', { - day: '2-digit', - month: 'short', - year: 'numeric', - hour: '2-digit', - minute: '2-digit' - }).format(date); - } catch (error) { - return "N/A"; - } - }; + const formatDate = (dateString: string | null | undefined) => { + if (!dateString) return "N/A"; + try { + const date = new Date(dateString); + return new Intl.DateTimeFormat('en-GB', { + day: '2-digit', + month: 'short', + year: 'numeric', + hour: '2-digit', + minute: '2-digit' + }).format(date); + } catch (error) { + return "N/A"; + } + }; - return ( - -
-
-

- - Customer Management -

+ return ( + +
+
+

+ + Customer Management +

+
+ + +
+
+
+
Show:
+ +
-
-
-
-
-
Show:
- -
-
- -
-
- -
- setSearchQuery(e.target.value)} - className="pl-10 pr-10 py-2 w-full bg-black/40 border-zinc-700 text-white" - /> - {searchQuery && ( - - )} -
- -
- {loading - ? "Loading..." - : searchQuery - ? `Found ${filteredCustomers.length} matching customers` - : `Showing ${filteredCustomers.length} of ${totalPages * itemsPerPage} customers`} -
+
+
+
+ setSearchQuery(e.target.value)} + className="pl-10 pr-10 py-2 w-full bg-background/50 border-border/50 focus:ring-primary/20 transition-all duration-300" + /> + {searchQuery && ( + + )} +
- {loading ? ( -
- {/* Loading indicator */} -
-
-
- - {/* Table skeleton */} -
-
- {['Customer', 'Orders', 'Total Spent', 'Last Order', 'Status'].map((header, i) => ( - - ))} -
- - {[...Array(5)].map((_, i) => ( -
-
- -
- - -
-
- - - - -
+
+ {loading + ? "Loading..." + : searchQuery + ? `Found ${filteredCustomers.length} matching customers` + : `Showing ${filteredCustomers.length} of ${totalPages * itemsPerPage} customers`} +
+
+ + + {loading ? ( +
+ {/* Loading indicator */} +
+
+
+ +
+
+ {['Customer', 'Orders', 'Total Spent', 'Last Order', 'Status'].map((header, i) => ( + ))}
+ + {[...Array(5)].map((_, i) => ( +
+
+ +
+ + +
+
+ + + + +
+ ))}
- ) : filteredCustomers.length === 0 ? ( -
- -

- {searchQuery ? "No customers matching your search" : "No customers found"} -

-

- {searchQuery - ? "Try a different search term or clear the search" - : "Once you have customers placing orders, they will appear here."} -

- {searchQuery && ( - - )} +
+ ) : filteredCustomers.length === 0 ? ( +
+
+
- ) : ( -
- - - - Customer - handleSort("totalOrders")} - > -
- Orders - -
-
- handleSort("totalSpent")} - > -
- Total Spent - -
-
- handleSort("lastOrderDate")} - > -
- Last Order - -
-
- Status -
-
- - {filteredCustomers.map((customer) => ( - + {searchQuery ? "No matching customers" : "No customers yet"} + +

+ {searchQuery + ? "We couldn't find any customers matching your search criteria." + : "Once you have customers placing orders, they will appear here."} +

+ {searchQuery && ( + + )} + + ) : ( +
+
+ + + Customer + handleSort("totalOrders")} + > +
+ Orders + +
+
+ handleSort("totalSpent")} + > +
+ Total Spent + +
+
+ handleSort("lastOrderDate")} + > +
+ Last Order + +
+
+ Status +
+
+ + + {filteredCustomers.map((customer, index) => ( + setSelectedCustomer(customer)} > - -
- @{customer.telegramUsername || "Unknown"} - {!customer.hasOrders && ( - - - New - - )} + +
+
+ {customer.telegramUsername ? customer.telegramUsername.substring(0, 2).toUpperCase() : 'ID'} +
+
+
+ @{customer.telegramUsername || "Unknown"} + {!customer.hasOrders && ( + + New + + )} +
+
+ ID: + {customer.telegramUserId} +
+
-
ID: {customer.telegramUserId}
- {customer.totalOrders} + + {customer.totalOrders} + - + {formatCurrency(customer.totalSpent)} - - {customer.lastOrderDate ? formatDate(customer.lastOrderDate) : "N/A"} + + {customer.lastOrderDate ? ( +
+ + {formatDate(customer.lastOrderDate).split(",")[0]} +
+ ) : "Never"}
{customer.hasOrders ? ( -
- - {customer.ordersByStatus.paid} Paid - - - {customer.ordersByStatus.completed} Completed - - - {customer.ordersByStatus.shipped} Shipped - +
+ {customer.ordersByStatus.paid > 0 && ( + + {customer.ordersByStatus.paid} Paid + + )} + {customer.ordersByStatus.completed > 0 && ( + + {customer.ordersByStatus.completed} Done + + )} + {customer.ordersByStatus.shipped > 0 && ( + + {customer.ordersByStatus.shipped} Ship + + )}
) : ( - - No orders yet - + No activity )} - + ))} - -
-
- )} + + + +
+ )} + -
-
- Page {page} of {totalPages} +
+
+ Page {page} of {totalPages} +
+
+ + + {totalPages > 2 ? ( + + + + + + {Array.from({ length: totalPages }, (_, i) => i + 1).map((pageNum) => ( + handlePageChange(pageNum)} + className={pageNum === page ? 'bg-primary/10 text-primary' : ''} + > + Page {pageNum} + + ))} + + + ) : null} + + +
+
+ + + {/* Customer Details Dialog */} + {selectedCustomer && ( + !open && setSelectedCustomer(null)}> + + + + Customer Details + + + +
+ {/* Customer Information */} +
+
+

Customer Information

+
+
+
Username:
+
@{selectedCustomer.telegramUsername || "Unknown"}
+
+
+
Telegram ID:
+
{selectedCustomer.telegramUserId}
+
+
+
Chat ID:
+
{selectedCustomer.chatId}
+
+
+
+ +
-
- - - {totalPages > 2 && ( - - - - - - {Array.from({ length: totalPages }, (_, i) => i + 1).map((pageNum) => ( - handlePageChange(pageNum)} - className={`cursor-pointer ${pageNum === page ? 'bg-zinc-800 text-white' : 'text-gray-300'}`} - > - Page {pageNum} - - ))} - - - )} - - + + {/* Order Statistics */} +
+

Order Statistics

+
+
+
Total Orders:
+
{selectedCustomer.totalOrders}
+
+
+
Total Spent:
+
{formatCurrency(selectedCustomer.totalSpent)}
+
+
+
First Order:
+
+ {formatDate(selectedCustomer.firstOrderDate)} +
+
+
+
Last Order:
+
+ {formatDate(selectedCustomer.lastOrderDate)} +
+
+
-
- {/* Customer Details Dialog */} - {selectedCustomer && ( - !open && setSelectedCustomer(null)}> - - - - Customer Details - - - -
- {/* Customer Information */} -
-
-

Customer Information

-
-
-
Username:
-
@{selectedCustomer.telegramUsername || "Unknown"}
-
-
-
Telegram ID:
-
{selectedCustomer.telegramUserId}
-
-
-
Chat ID:
-
{selectedCustomer.chatId}
-
-
-
- - -
- - {/* Order Statistics */} -
-

Order Statistics

-
-
-
Total Orders:
-
{selectedCustomer.totalOrders}
-
-
-
Total Spent:
-
{formatCurrency(selectedCustomer.totalSpent)}
-
-
-
First Order:
-
- {formatDate(selectedCustomer.firstOrderDate)} -
-
-
-
Last Order:
-
- {formatDate(selectedCustomer.lastOrderDate)} -
-
-
-
+ {/* Order Status Breakdown */} +
+

Order Status Breakdown

+
+
+

Paid

+

{selectedCustomer.ordersByStatus.paid}

- - {/* Order Status Breakdown */} -
-

Order Status Breakdown

-
-
-

Paid

-

{selectedCustomer.ordersByStatus.paid}

-
-
-

Acknowledged

-

{selectedCustomer.ordersByStatus.acknowledged}

-
-
-

Shipped

-

{selectedCustomer.ordersByStatus.shipped}

-
-
-

Completed

-

{selectedCustomer.ordersByStatus.completed}

-
-
+
+

Acknowledged

+

{selectedCustomer.ordersByStatus.acknowledged}

+
+

Shipped

+

{selectedCustomer.ordersByStatus.shipped}

+
+
+

Completed

+

{selectedCustomer.ordersByStatus.completed}

+
+
+
- - - - - -
- )} -
- - ); - } \ No newline at end of file + + + + + + + )} +
+ + ); +} \ No newline at end of file diff --git a/app/dashboard/storefront/page.tsx b/app/dashboard/storefront/page.tsx index 6eb9bda..471769d 100644 --- a/app/dashboard/storefront/page.tsx +++ b/app/dashboard/storefront/page.tsx @@ -6,11 +6,15 @@ import Layout from "@/components/layout/layout"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; -import { Save, Send, Key, MessageSquare, Shield, Globe, Wallet } from "lucide-react"; +import { Save, Send, Key, MessageSquare, Shield, Globe, Wallet, RefreshCw } from "lucide-react"; import { apiRequest } from "@/lib/api"; import { toast } from "sonner"; import BroadcastDialog from "@/components/modals/broadcast-dialog"; import Dashboard from "@/components/dashboard/dashboard"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle, CardFooter } from "@/components/ui/card"; +import { Label } from "@/components/ui/label"; +import { Badge } from "@/components/ui/badge"; +import { motion, AnimatePresence } from "framer-motion"; import { Select, SelectContent, @@ -166,251 +170,298 @@ export default function StorefrontPage() { return (
-
-
-

- - Storefront Settings -

-
- - - -
- - setStorefront((prev) => ({ - ...prev, - isEnabled: checked, - })) - } - /> - - {storefront.isEnabled ? 'Store Open' : 'Store Closed'} - -
-
- -

{storefront.isEnabled ? 'Click to close store' : 'Click to open store'}

-
-
-
+
+
+
+ +
+
+

+ Storefront Settings +

+

+ Manage your shop's appearance, policies, and configuration +

-
+
-
-
- {/* Security Settings */} -
-
-
- -

- Security -

-
-
-
- -