diff --git a/app/auth/login/components/LoginForm.tsx b/app/auth/login/components/LoginForm.tsx index 034068d..8512d8c 100644 --- a/app/auth/login/components/LoginForm.tsx +++ b/app/auth/login/components/LoginForm.tsx @@ -4,9 +4,9 @@ import { useState, useEffect, useRef } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import Link from "next/link"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; +import { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; +import { Label } from "@/components/common/label"; import { toast } from "sonner"; import { Loader2, ArrowRight } from "lucide-react"; import { motion } from "framer-motion"; @@ -186,4 +186,4 @@ export default function LoginForm() { ); -} \ No newline at end of file +} diff --git a/app/auth/register/page.tsx b/app/auth/register/page.tsx index b275b97..a98aaca 100644 --- a/app/auth/register/page.tsx +++ b/app/auth/register/page.tsx @@ -4,12 +4,12 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; import Image from "next/image"; import Link from "next/link"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; +import { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; +import { Label } from "@/components/common/label"; import { Loader2, ArrowRight } from "lucide-react"; import { motion } from "framer-motion"; -import { toast } from "@/hooks/use-toast"; +import { toast } from "@/lib/hooks/use-toast"; // Matches LoginPage background const AuthBackground = () => ( @@ -164,3 +164,5 @@ export default function RegisterPage() { ); } + + diff --git a/app/auth/reset-password/page.tsx b/app/auth/reset-password/page.tsx index 1fb5dd4..92c4b60 100644 --- a/app/auth/reset-password/page.tsx +++ b/app/auth/reset-password/page.tsx @@ -2,11 +2,11 @@ import { useState, useEffect, Suspense } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import Link from "next/link"; -import { fetchClient } from "@/lib/api-client"; +import { fetchClient } from "@/lib/api/api-client"; import { toast } from "sonner"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; +import { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; +import { Label } from "@/components/common/label"; import { Loader2 } from "lucide-react"; interface Vendor { @@ -193,3 +193,5 @@ export default function ResetPasswordPage() { ); } + + diff --git a/app/dashboard/admin/ban/page.tsx b/app/dashboard/admin/ban/page.tsx index 9f3f37e..098cc46 100644 --- a/app/dashboard/admin/ban/page.tsx +++ b/app/dashboard/admin/ban/page.tsx @@ -1,18 +1,18 @@ "use client"; import React, { useState, useEffect } from "react"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Textarea } from "@/components/ui/textarea"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { Badge } from "@/components/ui/badge"; -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; -import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/ui/alert-dialog"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; +import { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; +import { Label } from "@/components/common/label"; +import { Textarea } from "@/components/common/textarea"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/common/select"; +import { Badge } from "@/components/common/badge"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/common/table"; +import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger } from "@/components/common/alert-dialog"; import { UserX, Shield, Search, Ban, Unlock, Loader2 } from "lucide-react"; -import { fetchClient } from "@/lib/api-client"; -import { useToast } from "@/hooks/use-toast"; +import { fetchClient } from "@/lib/api/api-client"; +import { useToast } from "@/lib/hooks/use-toast"; interface BlockedUser { _id: string; @@ -445,4 +445,5 @@ export default function AdminBanPage() { ); -} \ No newline at end of file +} + diff --git a/app/dashboard/admin/invite/page.tsx b/app/dashboard/admin/invite/page.tsx index 9a1693a..d593d6e 100644 --- a/app/dashboard/admin/invite/page.tsx +++ b/app/dashboard/admin/invite/page.tsx @@ -1,13 +1,13 @@ "use client"; import React from "react"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Textarea } from "@/components/ui/textarea"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { Badge } from "@/components/ui/badge"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; +import { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; +import { Label } from "@/components/common/label"; +import { Textarea } from "@/components/common/textarea"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/common/select"; +import { Badge } from "@/components/common/badge"; import { UserPlus, Mail, Copy, Check } from "lucide-react"; import { useState } from "react"; import Link from "next/link"; @@ -206,3 +206,4 @@ export default function AdminInvitePage() { ); } + diff --git a/app/dashboard/admin/orders/page.tsx b/app/dashboard/admin/orders/page.tsx index 8da0c31..aa13fd2 100644 --- a/app/dashboard/admin/orders/page.tsx +++ b/app/dashboard/admin/orders/page.tsx @@ -1,9 +1,9 @@ import React from "react"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; import { Package, AlertTriangle, CheckCircle2, XCircle, DollarSign } from "lucide-react"; import { fetchServer } from "@/lib/api"; import OrdersTable from "@/components/admin/OrdersTable"; -import { MotionWrapper } from "@/components/ui/motion-wrapper"; +import { MotionWrapper } from "@/components/common/motion-wrapper"; export const dynamic = 'force-dynamic'; @@ -241,3 +241,5 @@ export default async function AdminOrdersPage() { ); } + + diff --git a/app/dashboard/admin/page.tsx b/app/dashboard/admin/page.tsx index def9a4e..0198514 100644 --- a/app/dashboard/admin/page.tsx +++ b/app/dashboard/admin/page.tsx @@ -2,12 +2,12 @@ export const dynamic = "force-dynamic"; import React, { Suspense, lazy, useState, useEffect, Component, ReactNode } from "react"; -import { Button } from "@/components/ui/button"; +import { Button } from "@/components/common/button"; import Link from "next/link"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { Skeleton } from "@/components/ui/skeleton"; -import { Card, CardContent, CardHeader } from "@/components/ui/card"; -import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/common/tabs"; +import { Skeleton } from "@/components/common/skeleton"; +import { Card, CardContent, CardHeader } from "@/components/common/card"; +import { Alert, AlertDescription, AlertTitle } from "@/components/common/alert"; import { AlertCircle, RefreshCw } from "lucide-react"; // Error Boundary Component @@ -466,3 +466,4 @@ export default function AdminPage() { } + diff --git a/app/dashboard/admin/status/page.tsx b/app/dashboard/admin/status/page.tsx index c9b73bf..383b6b4 100644 --- a/app/dashboard/admin/status/page.tsx +++ b/app/dashboard/admin/status/page.tsx @@ -1,10 +1,10 @@ import React from "react"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Badge } from "@/components/ui/badge"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/common/card"; +import { Badge } from "@/components/common/badge"; import { Server, Database, Cpu, HardDrive, Activity, Zap } from "lucide-react"; import { fetchServer } from "@/lib/api"; import SystemStatusCard from "@/components/admin/SystemStatusCard"; -import { MotionWrapper } from "@/components/ui/motion-wrapper"; +import { MotionWrapper } from "@/components/common/motion-wrapper"; export const dynamic = 'force-dynamic'; @@ -243,3 +243,5 @@ export default async function AdminStatusPage() { ); } + + diff --git a/app/dashboard/admin/users/page.tsx b/app/dashboard/admin/users/page.tsx index 30446ba..ed80df3 100644 --- a/app/dashboard/admin/users/page.tsx +++ b/app/dashboard/admin/users/page.tsx @@ -1,15 +1,15 @@ "use client"; import React, { useState, useEffect } from "react"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; +import { Badge } from "@/components/common/badge"; +import { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/common/table"; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/common/tooltip"; import { Search, Ban, UserCheck, Package, DollarSign, Loader2, Repeat, Users, ShoppingBag, CreditCard, UserX } from "lucide-react"; -import { fetchClient } from "@/lib/api-client"; -import { useToast } from "@/hooks/use-toast"; +import { fetchClient } from "@/lib/api/api-client"; +import { useToast } from "@/lib/hooks/use-toast"; import { motion, AnimatePresence } from "framer-motion"; interface TelegramUser { @@ -328,3 +328,5 @@ export default function AdminUsersPage() { ); } + + diff --git a/app/dashboard/admin/vendors/page.tsx b/app/dashboard/admin/vendors/page.tsx index e57d400..5a4a65b 100644 --- a/app/dashboard/admin/vendors/page.tsx +++ b/app/dashboard/admin/vendors/page.tsx @@ -1,16 +1,16 @@ "use client"; import React, { useState, useEffect, useCallback } from "react"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; +import { Badge } from "@/components/common/badge"; +import { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/common/table"; import { Search, MoreHorizontal, UserCheck, UserX, Mail, Loader2, Store, Shield, ShieldAlert, Clock, Calendar, Pencil, Plus } from "lucide-react"; -import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; -import { Label } from "@/components/ui/label"; -import { fetchClient } from "@/lib/api-client"; -import { useToast } from "@/hooks/use-toast"; +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/common/dialog"; +import { Label } from "@/components/common/label"; +import { fetchClient } from "@/lib/api/api-client"; +import { useToast } from "@/lib/hooks/use-toast"; import { motion, AnimatePresence } from "framer-motion"; import { DropdownMenu, @@ -19,7 +19,7 @@ import { DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; +} from "@/components/common/dropdown-menu"; interface Vendor { _id: string; @@ -571,3 +571,5 @@ export default function AdminVendorsPage() { ); } + + diff --git a/app/dashboard/analytics/loading.tsx b/app/dashboard/analytics/loading.tsx index c3a1483..7ba8343 100644 --- a/app/dashboard/analytics/loading.tsx +++ b/app/dashboard/analytics/loading.tsx @@ -1,5 +1,5 @@ -import { Skeleton } from "@/components/ui/skeleton"; -import { Card, CardContent, CardHeader } from "@/components/ui/card"; +import { Skeleton } from "@/components/common/skeleton"; +import { Card, CardContent, CardHeader } from "@/components/common/card"; import Layout from "@/components/layout/layout"; import { SnowLoader } from "@/components/snow-loader"; @@ -61,4 +61,4 @@ export default function AnalyticsLoading() { ); -} \ No newline at end of file +} diff --git a/app/dashboard/analytics/page.tsx b/app/dashboard/analytics/page.tsx index 45e5161..a28100f 100644 --- a/app/dashboard/analytics/page.tsx +++ b/app/dashboard/analytics/page.tsx @@ -5,7 +5,7 @@ import Dashboard from "@/components/dashboard/dashboard"; import AnalyticsDashboard from '@/components/analytics/AnalyticsDashboard'; import AnalyticsDashboardSkeleton from '@/components/analytics/AnalyticsDashboardSkeleton'; import StoreSelector from '@/components/analytics/StoreSelector'; -import { getAnalyticsOverviewServer } from '@/lib/server-api'; +import { getAnalyticsOverviewServer } from '@/lib/api/server-api'; import { fetchServer } from '@/lib/api'; import { performance } from 'perf_hooks'; import { Info, GitCommit, User, Zap, BarChart3 } from 'lucide-react'; @@ -34,14 +34,14 @@ export default async function AnalyticsPage({ // Await searchParams as required by Next.js 15+ const resolvedSearchParams = await searchParams; - + // Check for storeId in query parameters (for staff users) const storeId = resolvedSearchParams?.storeId; - + // Check for storeId in cookies (alternative method for staff users) const cookieStore = await cookies(); const cookieStoreId = cookieStore.get('storeId')?.value; - + // Use query parameter first, then cookie, then undefined (for vendors) const finalStoreId = storeId || cookieStoreId; @@ -73,7 +73,7 @@ export default async function AnalyticsPage({
- + v{panelVersion}
@@ -100,12 +100,12 @@ export default async function AnalyticsPage({ ); } catch (error) { console.error('Error fetching analytics data:', error); - + // If it's a 401/403 error, redirect to login if (error instanceof Error && error.message.includes('401')) { - redirect('/login'); + redirect('/auth/login'); } - + // If it's a 400 error (missing storeId for staff), show store selector if (error instanceof Error && error.message.includes('400')) { return ( @@ -122,7 +122,7 @@ export default async function AnalyticsPage({ ); } - + // For other errors, show a fallback return ( @@ -142,4 +142,4 @@ export default async function AnalyticsPage({ ); } -} \ No newline at end of file +} diff --git a/app/dashboard/balance/page.tsx b/app/dashboard/balance/page.tsx index 7e9faf3..6b10598 100644 --- a/app/dashboard/balance/page.tsx +++ b/app/dashboard/balance/page.tsx @@ -2,8 +2,8 @@ import { useState } from "react"; import Dashboard from "@/components/dashboard/dashboard"; -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/common/card"; +import { Button } from "@/components/common/button"; import { Wallet, Bitcoin, Coins, DollarSign, ArrowUpRight } from "lucide-react"; import { toast } from "sonner"; @@ -166,3 +166,4 @@ export default function BalancePage() { ); } + diff --git a/app/dashboard/categories/page.tsx b/app/dashboard/categories/page.tsx index f466c3d..3f54bd4 100644 --- a/app/dashboard/categories/page.tsx +++ b/app/dashboard/categories/page.tsx @@ -2,8 +2,8 @@ import { useState, useEffect, useRef } from "react"; import Layout from "@/components/layout/layout"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; +import { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; import { Plus, Pencil, Trash2, ChevronRight, ChevronDown, MoveVertical, FolderTree } from "lucide-react"; import { toast } from "sonner"; import { @@ -12,7 +12,7 @@ import { SelectItem, SelectTrigger, SelectValue, -} from "@/components/ui/select"; +} from "@/components/common/select"; import { AlertDialog, AlertDialogAction, @@ -22,10 +22,10 @@ import { AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, -} from "@/components/ui/alert-dialog"; +} from "@/components/common/alert-dialog"; import { apiRequest } from "@/lib/api"; -import type { Category } from "@/models/categories"; -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; +import type { Category } from "@/lib/models/categories"; +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/common/card"; import { motion, AnimatePresence } from "framer-motion"; // Drag and Drop imports @@ -494,4 +494,5 @@ export default function CategoriesPage() {
); -} \ No newline at end of file +} + diff --git a/app/dashboard/chats/[id]/loading.tsx b/app/dashboard/chats/[id]/loading.tsx index 06a3133..8b5294d 100644 --- a/app/dashboard/chats/[id]/loading.tsx +++ b/app/dashboard/chats/[id]/loading.tsx @@ -1,6 +1,6 @@ import Layout from "@/components/layout/layout"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Skeleton } from "@/components/ui/skeleton"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/common/card"; +import { Skeleton } from "@/components/common/skeleton"; import { Loader2 } from "lucide-react"; import { SnowLoader } from "@/components/snow-loader"; diff --git a/app/dashboard/chats/[id]/page.tsx b/app/dashboard/chats/[id]/page.tsx index 9e5bee1..00e1911 100644 --- a/app/dashboard/chats/[id]/page.tsx +++ b/app/dashboard/chats/[id]/page.tsx @@ -1,7 +1,7 @@ import React from "react"; import { Metadata } from "next"; import dynamic from "next/dynamic"; -import { Skeleton } from "@/components/ui/skeleton"; +import { Skeleton } from "@/components/common/skeleton"; import Dashboard from "@/components/dashboard/dashboard"; export const metadata: Metadata = { diff --git a/app/dashboard/chats/new/page.tsx b/app/dashboard/chats/new/page.tsx index 03c5723..e56fca2 100644 --- a/app/dashboard/chats/new/page.tsx +++ b/app/dashboard/chats/new/page.tsx @@ -1,7 +1,7 @@ import React from "react"; import { Metadata, Viewport } from "next"; import dynamic from "next/dynamic"; -import { Skeleton } from "@/components/ui/skeleton"; +import { Skeleton } from "@/components/common/skeleton"; import Dashboard from "@/components/dashboard/dashboard"; export const metadata: Metadata = { @@ -42,4 +42,4 @@ export default function NewChatPage() { ); -} \ No newline at end of file +} diff --git a/app/dashboard/chats/page.tsx b/app/dashboard/chats/page.tsx index f338905..0992960 100644 --- a/app/dashboard/chats/page.tsx +++ b/app/dashboard/chats/page.tsx @@ -5,10 +5,10 @@ import { useRouter } from "next/navigation"; import Dashboard from "@/components/dashboard/dashboard"; import { MessageCircle, AlertCircle, RefreshCw } from "lucide-react"; import dynamic from "next/dynamic"; -import { Skeleton } from "@/components/ui/skeleton"; -import { Card, CardContent, CardHeader } from "@/components/ui/card"; -import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import { Button } from "@/components/ui/button"; +import { Skeleton } from "@/components/common/skeleton"; +import { Card, CardContent, CardHeader } from "@/components/common/card"; +import { Alert, AlertDescription, AlertTitle } from "@/components/common/alert"; +import { Button } from "@/components/common/button"; // Error Boundary Component interface ErrorBoundaryState { @@ -95,7 +95,7 @@ function ChatTableSkeleton() { {/* Subtle loading indicator */}
-
- +
@@ -115,8 +115,8 @@ function ChatTableSkeleton() {
{['Customer', 'Last Message', 'Date', 'Status', 'Actions'].map((header, i) => ( -
- + {[...Array(6)].map((_, i) => ( -
{ - const authToken = document.cookie - .split("; ") - .find((row) => row.startsWith("Authorization=")) - ?.split("=")[1]; - - if (!authToken) { - router.push("/login"); - } - }, [router]); - return (
@@ -185,7 +172,7 @@ export default function ChatsPage() { Customer Chats
- + }> @@ -194,4 +181,4 @@ export default function ChatsPage() {
); -} \ No newline at end of file +} diff --git a/app/dashboard/dashboard-content-wrapper.tsx b/app/dashboard/dashboard-content-wrapper.tsx index bf0d4df..7726d3e 100644 --- a/app/dashboard/dashboard-content-wrapper.tsx +++ b/app/dashboard/dashboard-content-wrapper.tsx @@ -1,11 +1,11 @@ "use client"; import { Component, ReactNode, useState, useEffect, Suspense } from "react"; -import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import { Button } from "@/components/ui/button"; +import { Alert, AlertDescription, AlertTitle } from "@/components/common/alert"; +import { Button } from "@/components/common/button"; import { AlertCircle, RefreshCw } from "lucide-react"; -import { Skeleton } from "@/components/ui/skeleton"; -import { Card, CardContent, CardHeader } from "@/components/ui/card"; +import { Skeleton } from "@/components/common/skeleton"; +import { Card, CardContent, CardHeader } from "@/components/common/card"; // Error Boundary Component interface ErrorBoundaryState { @@ -260,3 +260,4 @@ export default function DashboardContentWrapper({ children }: { children: ReactN ); } + diff --git a/app/dashboard/loading.tsx b/app/dashboard/loading.tsx index 6a55e5c..b1db624 100644 --- a/app/dashboard/loading.tsx +++ b/app/dashboard/loading.tsx @@ -1,7 +1,7 @@ "use client" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Skeleton } from "@/components/ui/skeleton"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; +import { Skeleton } from "@/components/common/skeleton"; import Layout from "@/components/layout/layout"; import { Loader2 } from "lucide-react"; import { SnowLoader } from "@/components/snow-loader"; @@ -77,4 +77,4 @@ export default function DashboardLoading() {
); -} \ No newline at end of file +} diff --git a/app/dashboard/orders/[id]/loading.tsx b/app/dashboard/orders/[id]/loading.tsx index 8fd214e..7d16b93 100644 --- a/app/dashboard/orders/[id]/loading.tsx +++ b/app/dashboard/orders/[id]/loading.tsx @@ -1,7 +1,7 @@ "use client" -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Skeleton } from "@/components/ui/skeleton"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/common/card"; +import { Skeleton } from "@/components/common/skeleton"; import Layout from "@/components/layout/layout"; import { Loader2 } from "lucide-react"; import { SnowLoader } from "@/components/snow-loader"; diff --git a/app/dashboard/orders/[id]/page.tsx b/app/dashboard/orders/[id]/page.tsx index 2ebd044..4913e6d 100644 --- a/app/dashboard/orders/[id]/page.tsx +++ b/app/dashboard/orders/[id]/page.tsx @@ -4,10 +4,10 @@ import { fetchData } from '@/lib/api'; import { clientFetch } from '@/lib/api'; import { useEffect, useState } from "react"; import { useParams } from "next/navigation"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Textarea } from "@/components/ui/textarea"; +import { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; +import { Label } from "@/components/common/label"; +import { Textarea } from "@/components/common/textarea"; import { Table, TableBody, @@ -15,13 +15,13 @@ import { TableHead, TableHeader, TableRow, -} from "@/components/ui/table"; +} from "@/components/common/table"; import { Card, CardContent, CardHeader, CardTitle, -} from "@/components/ui/card"; +} from "@/components/common/card"; import { Clipboard, Truck, Package, ArrowRight, ChevronDown, AlertTriangle, Copy, Loader2, RefreshCw, MessageCircle } from "lucide-react"; import Link from "next/link"; import { useRouter } from "next/navigation"; @@ -36,9 +36,9 @@ import { AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, -} from "@/components/ui/alert-dialog"; +} from "@/components/common/alert-dialog"; import Layout from "@/components/layout/layout"; -import { cacheUtils } from '@/lib/api-client'; +import { cacheUtils } from '@/lib/api/api-client'; import OrderTimeline from "@/components/orders/order-timeline"; import { motion, AnimatePresence } from "framer-motion"; diff --git a/app/dashboard/orders/page.tsx b/app/dashboard/orders/page.tsx index e6428a3..9e5b36e 100644 --- a/app/dashboard/orders/page.tsx +++ b/app/dashboard/orders/page.tsx @@ -5,10 +5,10 @@ import { useRouter } from "next/navigation"; import Dashboard from "@/components/dashboard/dashboard"; import { Package, AlertCircle, RefreshCw } from "lucide-react"; import dynamic from "next/dynamic"; -import { Skeleton } from "@/components/ui/skeleton"; -import { Card, CardContent, CardHeader } from "@/components/ui/card"; -import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import { Button } from "@/components/ui/button"; +import { Skeleton } from "@/components/common/skeleton"; +import { Card, CardContent, CardHeader } from "@/components/common/card"; +import { Alert, AlertDescription, AlertTitle } from "@/components/common/alert"; +import { Button } from "@/components/common/button"; // Error Boundary Component interface ErrorBoundaryState { @@ -95,7 +95,7 @@ function OrderTableSkeleton() { {/* Subtle loading indicator */}
-
- +
@@ -119,8 +119,8 @@ function OrderTableSkeleton() {
{['Order ID', 'Customer', 'Status', 'Total', 'Date', 'Actions'].map((header, i) => ( -
- + {/* Table rows skeleton */} {[...Array(8)].map((_, i) => ( -
{ - const authToken = document.cookie - .split("; ") - .find((row) => row.startsWith("Authorization=")) - ?.split("=")[1]; - - if (!authToken) { - router.push("/login"); - } - }, [router]); - return (
@@ -194,4 +181,4 @@ export default function OrdersPage() {
); -} \ No newline at end of file +} diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index c71afc7..318b772 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -6,8 +6,8 @@ import packageJson from '../../package.json'; import { getGitInfo, getShortGitHash } from '@/lib/utils/git'; import { Suspense } from 'react'; import dynamic from 'next/dynamic'; -import { Skeleton } from '@/components/ui/skeleton'; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Skeleton } from '@/components/common/skeleton'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/common/card'; import DashboardContentWrapper from './dashboard-content-wrapper'; // Loading skeleton for the dashboard content @@ -182,4 +182,5 @@ export default async function DashboardPage() {
); -} \ No newline at end of file +} + diff --git a/app/dashboard/products/page.tsx b/app/dashboard/products/page.tsx index f6aa4a7..5547692 100644 --- a/app/dashboard/products/page.tsx +++ b/app/dashboard/products/page.tsx @@ -3,16 +3,16 @@ import { useState, useEffect, ChangeEvent, Suspense } from "react"; import { useRouter } from "next/navigation"; import Layout from "@/components/layout/layout"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Product } from "@/models/products"; +import { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; +import { Product } from "@/lib/models/products"; import { Plus, Upload, Search, RefreshCw, Package2 } from "lucide-react"; import { clientFetch } from "@/lib/api"; -import { Category } from "@/models/categories"; +import { Category } from "@/lib/models/categories"; import { toast } from "sonner"; import dynamic from "next/dynamic"; -import { Skeleton } from "@/components/ui/skeleton"; -import { Card, CardContent, CardHeader } from "@/components/ui/card"; +import { Skeleton } from "@/components/common/skeleton"; +import { Card, CardContent, CardHeader } from "@/components/common/card"; // Lazy load heavy components with error handling const ProductTable = dynamic(() => import("@/components/tables/product-table").catch((err) => { @@ -156,16 +156,6 @@ export default function ProductsPage() { // Fetch products and categories useEffect(() => { - const authToken = document.cookie - .split("; ") - .find((row) => row.startsWith("Authorization=")) - ?.split("=")[1]; - - if (!authToken) { - router.push("/login"); - return; - } - const fetchDataAsync = async () => { try { setLoading(true); @@ -194,7 +184,7 @@ export default function ProductsPage() { }; fetchDataAsync(); - }, [router]); + }, []); const handleAddTier = () => { setProductData((prev) => ({ @@ -552,3 +542,5 @@ export default function ProductsPage() { ); } + + diff --git a/app/dashboard/shipping/page.tsx b/app/dashboard/shipping/page.tsx index 6b58cf4..f494e0a 100644 --- a/app/dashboard/shipping/page.tsx +++ b/app/dashboard/shipping/page.tsx @@ -4,8 +4,8 @@ import { useState, useEffect, ChangeEvent, Suspense } from "react"; import { useRouter } from "next/navigation"; import Layout from "@/components/layout/layout"; import { Edit, Plus, Trash, Truck } from "lucide-react"; -import { Button } from "@/components/ui/button"; -import { Skeleton } from "@/components/ui/skeleton"; +import { Button } from "@/components/common/button"; +import { Skeleton } from "@/components/common/skeleton"; import { fetchShippingMethods, addShippingMethod, @@ -15,7 +15,7 @@ import { ShippingData } from "@/lib/services/shipping-service"; import dynamic from "next/dynamic"; -import { Card, CardContent, CardHeader } from "@/components/ui/card"; +import { Card, CardContent, CardHeader } from "@/components/common/card"; // Lazy load components with error handling const ShippingModal = dynamic(() => import("@/components/modals/shipping-modal").then(mod => ({ default: mod.ShippingModal })).catch((err) => { @@ -142,11 +142,6 @@ export default function ShippingPage() { .find((row) => row.startsWith("Authorization=")) ?.split("=")[1]; - if (!authToken) { - router.push("/login"); - return; - } - const fetchedMethods: ShippingMethod[] = await fetchShippingMethods( authToken ); @@ -317,4 +312,4 @@ export default function ShippingPage() { /> ); -} \ No newline at end of file +} diff --git a/app/dashboard/stock/page.tsx b/app/dashboard/stock/page.tsx index 1abeb46..43cd6d3 100644 --- a/app/dashboard/stock/page.tsx +++ b/app/dashboard/stock/page.tsx @@ -3,12 +3,12 @@ import { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; import Layout from "@/components/layout/layout"; -import { Button } from "@/components/ui/button"; -import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from "@/components/ui/table"; -import { Input } from "@/components/ui/input"; -import { Switch } from "@/components/ui/switch"; -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; -import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/common/button"; +import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from "@/components/common/table"; +import { Input } from "@/components/common/input"; +import { Switch } from "@/components/common/switch"; +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/common/card"; +import { Badge } from "@/components/common/badge"; import { DropdownMenu, DropdownMenuContent, @@ -16,12 +16,12 @@ import { DropdownMenuTrigger, DropdownMenuLabel, DropdownMenuSeparator -} from "@/components/ui/dropdown-menu"; +} from "@/components/common/dropdown-menu"; import { Popover, PopoverContent, PopoverTrigger, -} from "@/components/ui/popover"; +} from "@/components/common/popover"; import { AlertDialog, AlertDialogAction, @@ -32,12 +32,12 @@ import { AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, -} from "@/components/ui/alert-dialog"; -import { Product } from "@/models/products"; +} from "@/components/common/alert-dialog"; +import { Product } from "@/lib/models/products"; import { Package, RefreshCw, ChevronDown, CheckSquare, XSquare, Boxes, Download, Calendar, Search, Filter, Save, X, Edit2 } from "lucide-react"; import { clientFetch } from "@/lib/api"; import { toast } from "sonner"; -import { DatePicker, DateRangePicker, DateRangeDisplay, MonthPicker } from "@/components/ui/date-picker"; +import { DatePicker, DateRangePicker, DateRangeDisplay, MonthPicker } from "@/components/common/date-picker"; import { DateRange } from "react-day-picker"; import { addDays, startOfDay, endOfDay, format, isSameDay } from "date-fns"; import { motion, AnimatePresence } from "framer-motion"; @@ -72,16 +72,6 @@ export default function StockManagementPage() { const [isExporting, setIsExporting] = useState(false); useEffect(() => { - const authToken = document.cookie - .split("; ") - .find((row) => row.startsWith("Authorization=")) - ?.split("=")[1]; - - if (!authToken) { - router.push("/login"); - return; - } - const fetchDataAsync = async () => { try { const response = await clientFetch('api/products'); @@ -105,7 +95,7 @@ export default function StockManagementPage() { }; fetchDataAsync(); - }, [router]); + }, []); const handleEditStock = (productId: string) => { setEditingStock({ @@ -687,4 +677,5 @@ export default function StockManagementPage() { ); -} \ No newline at end of file +} + diff --git a/app/dashboard/storefront/customers/page.tsx b/app/dashboard/storefront/customers/page.tsx index 360fed3..a37bb5c 100644 --- a/app/dashboard/storefront/customers/page.tsx +++ b/app/dashboard/storefront/customers/page.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState, useCallback } from "react"; import { getCustomers, type CustomerStats } from "@/lib/api"; -import { formatCurrency } from "@/utils/format"; +import { formatCurrency } from "@/lib/utils/format"; import { useRouter } from "next/navigation"; import { toast } from "sonner"; import Layout from "@/components/layout/layout"; @@ -13,14 +13,14 @@ import { TableHead, TableHeader, TableRow, -} from "@/components/ui/table"; +} from "@/components/common/table"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from "@/components/ui/select"; +} from "@/components/common/select"; import { Dialog, DialogContent, @@ -28,8 +28,8 @@ import { DialogTitle, DialogDescription, DialogFooter, -} from "@/components/ui/dialog"; -import { Button } from "@/components/ui/button"; +} from "@/components/common/dialog"; +import { Button } from "@/components/common/button"; import { ChevronLeft, ChevronRight, @@ -47,17 +47,17 @@ import { Truck, CheckCircle, } 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 { Badge } from "@/components/common/badge"; +import { Input } from "@/components/common/input"; +import { Skeleton } from "@/components/common/skeleton"; +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/common/card"; import { motion, AnimatePresence } from "framer-motion"; import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, -} from "@/components/ui/dropdown-menu"; +} from "@/components/common/dropdown-menu"; export default function CustomerManagementPage() { const router = useRouter(); @@ -126,16 +126,6 @@ export default function CustomerManagementPage() { fetchCustomers(); }, [fetchCustomers]); - useEffect(() => { - const authToken = document.cookie - .split("; ") - .find((row) => row.startsWith("Authorization=")) - ?.split("=")[1]; - - if (!authToken) { - router.push("/login"); - } - }, [router]); // Add filter function to filter customers when search query changes useEffect(() => { @@ -716,4 +706,5 @@ export default function CustomerManagementPage() {
); -} \ No newline at end of file +} + diff --git a/app/dashboard/storefront/page.tsx b/app/dashboard/storefront/page.tsx index 471769d..16de8bc 100644 --- a/app/dashboard/storefront/page.tsx +++ b/app/dashboard/storefront/page.tsx @@ -3,17 +3,17 @@ import { useState, useEffect, ChangeEvent } from "react"; import { useRouter } from "next/navigation"; 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 { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; +import { Textarea } from "@/components/common/textarea"; 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 { Card, CardContent, CardDescription, CardHeader, CardTitle, CardFooter } from "@/components/common/card"; +import { Label } from "@/components/common/label"; +import { Badge } from "@/components/common/badge"; import { motion, AnimatePresence } from "framer-motion"; import { Select, @@ -21,9 +21,9 @@ import { SelectItem, SelectTrigger, SelectValue, -} from "@/components/ui/select"; -import { Switch } from "@/components/ui/switch"; -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; +} from "@/components/common/select"; +import { Switch } from "@/components/common/switch"; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/common/tooltip"; const SHIPPING_REGIONS = [ { value: "UK", label: "United Kingdom", emoji: "🇬🇧" }, @@ -106,16 +106,6 @@ export default function StorefrontPage() { const [saving, setSaving] = useState(false); useEffect(() => { - const authToken = document.cookie - .split("; ") - .find((row) => row.startsWith("Authorization=")) - ?.split("=")[1]; - - if (!authToken) { - router.push("/login"); - return; - } - const fetchStorefront = async () => { try { setLoading(true); @@ -465,3 +455,5 @@ export default function StorefrontPage() { ); } + + diff --git a/app/not-found.tsx b/app/not-found.tsx index 457f036..eb2d17d 100644 --- a/app/not-found.tsx +++ b/app/not-found.tsx @@ -1,6 +1,6 @@ import { Metadata, Viewport } from "next"; import Link from "next/link"; -import { buttonVariants } from "@/components/ui/button"; +import { buttonVariants } from "@/components/common/button"; import { ArrowLeft } from "lucide-react"; export const metadata: Metadata = { @@ -38,3 +38,4 @@ export default function NotFound() {
); } + diff --git a/app/page.tsx b/app/page.tsx index 733e6d8..094156f 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,10 +1,10 @@ -import { getPlatformStatsServer } from "@/lib/server-api"; -import { HomeNavbar } from "@/components/home-navbar"; +import { getPlatformStatsServer } from "@/lib/api/server-api"; +import { HomeNavbar } from "@/components/layout/home-navbar"; import { Shield, LineChart, Zap, ArrowRight, Sparkles } from "lucide-react"; -import { Button } from "@/components/ui/button"; +import { Button } from "@/components/common/button"; import Link from "next/link"; import { AnimatedStatsSection } from "@/components/animated-stats-section"; -import { MotionWrapper } from "@/components/ui/motion-wrapper"; +import { MotionWrapper } from "@/components/common/motion-wrapper"; export const dynamic = 'force-dynamic'; @@ -109,3 +109,6 @@ export default async function Home() { return
Error loading page
; } } + + + diff --git a/components/KeepOnline.ts b/components/KeepOnline.ts index fe53598..0e7bc7b 100644 --- a/components/KeepOnline.ts +++ b/components/KeepOnline.ts @@ -1,6 +1,6 @@ "use client"; -import { useKeepOnline } from "@/hooks/useKeepOnline"; +import { useKeepOnline } from "@/lib/hooks/useKeepOnline"; const KeepOnline = () => { useKeepOnline({ @@ -14,4 +14,4 @@ const KeepOnline = () => { return null; } -export default KeepOnline; \ No newline at end of file +export default KeepOnline; diff --git a/components/admin/AdminAnalytics.tsx b/components/admin/AdminAnalytics.tsx index 36c946e..e48ba7e 100644 --- a/components/admin/AdminAnalytics.tsx +++ b/components/admin/AdminAnalytics.tsx @@ -7,16 +7,16 @@ import { CardDescription, CardHeader, CardTitle, -} from "@/components/ui/card"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +} from "@/components/common/card"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/common/tabs"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from "@/components/ui/select"; -import { Button } from "@/components/ui/button"; +} from "@/components/common/select"; +import { Button } from "@/components/common/button"; import { AlertCircle, BarChart, @@ -29,8 +29,8 @@ import { Package, Trophy, } from "lucide-react"; -import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import { fetchClient } from "@/lib/api-client"; +import { Alert, AlertDescription, AlertTitle } from "@/components/common/alert"; +import { fetchClient } from "@/lib/api/api-client"; import { BarChart as RechartsBarChart, Bar, @@ -43,7 +43,7 @@ import { Line, ComposedChart, } from "recharts"; -import { formatGBP } from "@/utils/format"; +import { formatGBP, formatNumber } from "@/lib/utils/format"; import { PieChart, Pie, Cell, Legend } from "recharts"; interface GrowthData { @@ -691,7 +691,7 @@ export default function AdminAnalytics() { {formatCurrency(bestMonth.revenue)}
- {bestMonth.orders.toLocaleString()} orders + {formatNumber(bestMonth.orders)} orders
@@ -714,7 +714,7 @@ export default function AdminAnalytics() {
- {analyticsData?.orders?.total?.toLocaleString() || "0"} + {formatNumber(analyticsData?.orders?.total)}
Today: {analyticsData?.orders?.totalToday || 0} @@ -876,7 +876,7 @@ export default function AdminAnalytics() {
- {analyticsData?.products?.total?.toLocaleString() || "0"} + {formatNumber(analyticsData?.products?.total)}
New This Week: {analyticsData?.products?.recent || 0} @@ -1096,27 +1096,25 @@ export default function AdminAnalytics() {
Total Vendors
- {analyticsData?.vendors?.total?.toLocaleString() || "0"} + {formatNumber(analyticsData?.vendors?.total)}
Active Vendors
- {analyticsData?.vendors?.active?.toLocaleString() || "0"} + {formatNumber(analyticsData?.vendors?.active)}
Active Stores
- {analyticsData?.vendors?.activeStores?.toLocaleString() || - "0"} + {formatNumber(analyticsData?.vendors?.activeStores)}
New This Week
- {analyticsData?.vendors?.newThisWeek?.toLocaleString() || - "0"} + {formatNumber(analyticsData?.vendors?.newThisWeek)}
@@ -1202,7 +1200,7 @@ export default function AdminAnalytics() {
Total Orders
-
{growthData.cumulative.orders.toLocaleString()}
+
{formatNumber(growthData.cumulative.orders)}
@@ -1214,19 +1212,19 @@ export default function AdminAnalytics() {
Customers
-
{growthData.cumulative.customers.toLocaleString()}
+
{formatNumber(growthData.cumulative.customers)}
Vendors
-
{growthData.cumulative.vendors.toLocaleString()}
+
{formatNumber(growthData.cumulative.vendors)}
Products
-
{growthData.cumulative.products.toLocaleString()}
+
{formatNumber(growthData.cumulative.products)}
@@ -1292,7 +1290,7 @@ export default function AdminAnalytics() {
Orders - {data.orders.toLocaleString()} + {formatNumber(data.orders)}
@@ -1304,7 +1302,7 @@ export default function AdminAnalytics() {
Customers - {data.customers.toLocaleString()} + {formatNumber(data.customers)}
@@ -1554,3 +1552,5 @@ export default function AdminAnalytics() {
); } + + diff --git a/components/admin/BanUserCard.tsx b/components/admin/BanUserCard.tsx index 7ff4cf4..0cf44f4 100644 --- a/components/admin/BanUserCard.tsx +++ b/components/admin/BanUserCard.tsx @@ -1,6 +1,6 @@ "use client"; import { useState } from "react"; -import { fetchClient } from "@/lib/api-client"; +import { fetchClient } from "@/lib/api/api-client"; export default function BanUserCard() { const [telegramUserId, setTelegramUserId] = useState(""); @@ -70,3 +70,4 @@ export default function BanUserCard() { } + diff --git a/components/admin/InvitationsListCard.tsx b/components/admin/InvitationsListCard.tsx index 18172d3..b609d2b 100644 --- a/components/admin/InvitationsListCard.tsx +++ b/components/admin/InvitationsListCard.tsx @@ -1,6 +1,6 @@ "use client"; import { useEffect, useState } from "react"; -import { fetchClient } from "@/lib/api-client"; +import { fetchClient } from "@/lib/api/api-client"; interface Invitation { _id: string; @@ -88,3 +88,4 @@ export default function InvitationsListCard() { } + diff --git a/components/admin/InviteVendorCard.tsx b/components/admin/InviteVendorCard.tsx index 0732f58..50204bc 100644 --- a/components/admin/InviteVendorCard.tsx +++ b/components/admin/InviteVendorCard.tsx @@ -1,9 +1,9 @@ import { useState } from "react"; -import { fetchClient } from "@/lib/api-client"; -import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; +import { fetchClient } from "@/lib/api/api-client"; +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/common/card"; +import { Button } from "@/components/common/button"; import { Copy, Check, Ticket, Loader2, RefreshCw } from "lucide-react"; -import { useToast } from "@/hooks/use-toast"; +import { useToast } from "@/lib/hooks/use-toast"; export default function InviteVendorCard() { const [loading, setLoading] = useState(false); @@ -103,3 +103,5 @@ export default function InviteVendorCard() { } + + diff --git a/components/admin/OrderDetailsModal.tsx b/components/admin/OrderDetailsModal.tsx index cda7277..a6d2184 100644 --- a/components/admin/OrderDetailsModal.tsx +++ b/components/admin/OrderDetailsModal.tsx @@ -7,13 +7,13 @@ import { DialogDescription, DialogHeader, DialogTitle, -} from "@/components/ui/dialog"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Badge } from "@/components/ui/badge"; -import { Skeleton } from "@/components/ui/skeleton"; -import { Button } from "@/components/ui/button"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { fetchClient } from "@/lib/api-client"; +} from "@/components/common/dialog"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; +import { Badge } from "@/components/common/badge"; +import { Skeleton } from "@/components/common/skeleton"; +import { Button } from "@/components/common/button"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/common/select"; +import { fetchClient } from "@/lib/api/api-client"; import { toast } from "sonner"; import { Package, User, Calendar, DollarSign, MapPin, Truck, CheckCircle, XCircle, Clock, Wallet, Copy, ExternalLink } from "lucide-react"; @@ -562,3 +562,5 @@ export default function OrderDetailsModal({ orderId, open, onOpenChange }: Order ); } + + diff --git a/components/admin/OrdersTable.tsx b/components/admin/OrdersTable.tsx index 9cceb19..fc35ac1 100644 --- a/components/admin/OrdersTable.tsx +++ b/components/admin/OrdersTable.tsx @@ -1,11 +1,11 @@ "use client"; import { useState, useMemo } from "react"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; +import { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/common/table"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/common/select"; import { Search, Filter, Eye, ChevronLeft, ChevronRight } from "lucide-react"; import { List } from 'react-window'; import OrderDetailsModal from "./OrderDetailsModal"; @@ -366,3 +366,4 @@ export default function OrdersTable({ orders, enableModal = true }: OrdersTableP ); } + diff --git a/components/admin/RecentOrdersCard.tsx b/components/admin/RecentOrdersCard.tsx index 6f4c5f6..18503b1 100644 --- a/components/admin/RecentOrdersCard.tsx +++ b/components/admin/RecentOrdersCard.tsx @@ -1,6 +1,6 @@ "use client"; import { useEffect, useState } from "react"; -import { fetchClient } from "@/lib/api-client"; +import { fetchClient } from "@/lib/api/api-client"; interface OrderItem { name: string; @@ -99,3 +99,4 @@ export default function RecentOrdersCard() { } + diff --git a/components/admin/SystemStatusCard.tsx b/components/admin/SystemStatusCard.tsx index f035127..b0badd5 100644 --- a/components/admin/SystemStatusCard.tsx +++ b/components/admin/SystemStatusCard.tsx @@ -1,8 +1,8 @@ "use client"; import { useEffect, useState } from "react"; -import { fetchClient } from "@/lib/api-client"; -import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { fetchClient } from "@/lib/api/api-client"; +import { Button } from "@/components/common/button"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; interface Status { uptimeSeconds: number; @@ -151,3 +151,5 @@ export default function SystemStatusCard() { } + + diff --git a/components/admin/VendorsCard.tsx b/components/admin/VendorsCard.tsx index 1034254..832f1d0 100644 --- a/components/admin/VendorsCard.tsx +++ b/components/admin/VendorsCard.tsx @@ -1,6 +1,6 @@ "use client"; import { useEffect, useState } from "react"; -import { fetchClient } from "@/lib/api-client"; +import { fetchClient } from "@/lib/api/api-client"; interface Vendor { _id: string; @@ -137,3 +137,4 @@ export default function VendorsCard() {
); } + diff --git a/components/analytics/AnalyticsDashboard.tsx b/components/analytics/AnalyticsDashboard.tsx index 9450aa8..6b56cf5 100644 --- a/components/analytics/AnalyticsDashboard.tsx +++ b/components/analytics/AnalyticsDashboard.tsx @@ -7,17 +7,17 @@ import { CardDescription, CardHeader, CardTitle, -} from "@/components/ui/card"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; +} from "@/components/common/card"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/common/tabs"; +import { Badge } from "@/components/common/badge"; +import { Button } from "@/components/common/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from "@/components/ui/select"; +} from "@/components/common/select"; import { TrendingUp, ShoppingCart, @@ -32,21 +32,21 @@ import { EyeOff, Calculator, } from "lucide-react"; -import { useToast } from "@/hooks/use-toast"; +import { useToast } from "@/lib/hooks/use-toast"; import MetricsCard from "./MetricsCard"; import { getAnalyticsOverviewWithStore, type AnalyticsOverview, } from "@/lib/services/analytics-service"; -import { formatGBP } from "@/utils/format"; +import { formatGBP, formatNumber } from "@/lib/utils/format"; import { MetricsCardSkeleton } from "./SkeletonLoaders"; import dynamic from "next/dynamic"; -import { Skeleton } from "@/components/ui/skeleton"; -import { DateRangePicker } from "@/components/ui/date-picker"; +import { Skeleton } from "@/components/common/skeleton"; +import { DateRangePicker } from "@/components/common/date-picker"; import { DateRange } from "react-day-picker"; import { addDays, startOfDay, endOfDay } from "date-fns"; import type { DateRange as ProfitDateRange } from "@/lib/services/profit-analytics-service"; -import { MotionWrapper } from "@/components/ui/motion-wrapper"; +import { MotionWrapper } from "@/components/common/motion-wrapper"; import { motion } from "framer-motion"; const RevenueChart = dynamic(() => import("./RevenueChart"), { @@ -170,7 +170,7 @@ export default function AnalyticsDashboard({ }, { title: "Total Orders", - value: maskValue(data.orders.total.toLocaleString()), + value: maskValue(formatNumber(data.orders.total)), description: "All-time orders", icon: ShoppingCart, trend: data.orders.completed > 0 ? ("up" as const) : ("neutral" as const), @@ -178,7 +178,7 @@ export default function AnalyticsDashboard({ }, { title: "Unique Customers", - value: maskValue(data.customers.unique.toLocaleString()), + value: maskValue(formatNumber(data.customers.unique)), description: "Total customers", icon: Users, trend: "neutral" as const, @@ -186,7 +186,7 @@ export default function AnalyticsDashboard({ }, { title: "Products", - value: maskValue(data.products.total.toLocaleString()), + value: maskValue(formatNumber(data.products.total)), description: "Active products", icon: Package, trend: "neutral" as const, @@ -451,3 +451,5 @@ export default function AnalyticsDashboard({
); } + + diff --git a/components/analytics/AnalyticsDashboardSkeleton.tsx b/components/analytics/AnalyticsDashboardSkeleton.tsx index ea4e8ad..9892af3 100644 --- a/components/analytics/AnalyticsDashboardSkeleton.tsx +++ b/components/analytics/AnalyticsDashboardSkeleton.tsx @@ -1,6 +1,6 @@ -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Skeleton } from "@/components/ui/skeleton"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; +import { Skeleton } from "@/components/common/skeleton"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/common/tabs"; import { MetricsCardSkeleton } from './SkeletonLoaders'; import { TrendingUp, @@ -201,4 +201,4 @@ export default function AnalyticsDashboardSkeleton() { ); -} \ No newline at end of file +} diff --git a/components/analytics/CustomerInsightsChart.tsx b/components/analytics/CustomerInsightsChart.tsx index 04830b4..85d7f96 100644 --- a/components/analytics/CustomerInsightsChart.tsx +++ b/components/analytics/CustomerInsightsChart.tsx @@ -1,15 +1,15 @@ "use client" import { useState, useEffect } from 'react'; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { useToast } from "@/hooks/use-toast"; -import { Skeleton } from "@/components/ui/skeleton"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; +import { Badge } from "@/components/common/badge"; +import { Button } from "@/components/common/button"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/common/select"; +import { useToast } from "@/lib/hooks/use-toast"; +import { Skeleton } from "@/components/common/skeleton"; import { Users, Crown, UserPlus, UserCheck, Star, ChevronLeft, ChevronRight } from "lucide-react"; import { getCustomerInsightsWithStore, type CustomerInsights } from "@/lib/services/analytics-service"; -import { formatGBP } from "@/utils/format"; +import { formatGBP } from "@/lib/utils/format"; import { CustomerInsightsSkeleton } from './SkeletonLoaders'; export default function CustomerInsightsChart() { @@ -305,4 +305,5 @@ export default function CustomerInsightsChart() {
); -} \ No newline at end of file +} + diff --git a/components/analytics/GrowthAnalyticsChart.tsx b/components/analytics/GrowthAnalyticsChart.tsx index 52289de..251c5f4 100644 --- a/components/analytics/GrowthAnalyticsChart.tsx +++ b/components/analytics/GrowthAnalyticsChart.tsx @@ -7,9 +7,9 @@ import { CardDescription, CardHeader, CardTitle, -} from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; -import { useToast } from "@/hooks/use-toast"; +} from "@/components/common/card"; +import { Button } from "@/components/common/button"; +import { useToast } from "@/lib/hooks/use-toast"; import { RefreshCw } from "lucide-react"; import { getGrowthAnalyticsWithStore, @@ -26,6 +26,7 @@ import { ResponsiveContainer, Area, } from "recharts"; +import { formatGBP, formatNumber } from "@/lib/utils/format"; interface GrowthAnalyticsChartProps { hideNumbers?: boolean; @@ -63,14 +64,6 @@ export default function GrowthAnalyticsChart({ fetchGrowthData(); }; - const formatCurrency = (value: number) => { - if (hideNumbers) return "£***"; - return new Intl.NumberFormat("en-GB", { - style: "currency", - currency: "GBP", - maximumFractionDigits: 0, - }).format(value); - }; return (
@@ -115,9 +108,7 @@ export default function GrowthAnalyticsChart({ Total Orders
- {hideNumbers - ? "***" - : growthData.cumulative.orders.toLocaleString()} + {hideNumbers ? "***" : formatNumber(growthData.cumulative.orders)}
@@ -127,7 +118,7 @@ export default function GrowthAnalyticsChart({ Total Revenue
- {formatCurrency(growthData.cumulative.revenue)} + {hideNumbers ? "£***" : formatGBP(growthData.cumulative.revenue)}
@@ -137,9 +128,7 @@ export default function GrowthAnalyticsChart({ Customers
- {hideNumbers - ? "***" - : growthData.cumulative.customers.toLocaleString()} + {hideNumbers ? "***" : formatNumber(growthData.cumulative.customers)}
@@ -149,9 +138,7 @@ export default function GrowthAnalyticsChart({ Products
- {hideNumbers - ? "***" - : growthData.cumulative.products.toLocaleString()} + {hideNumbers ? "***" : formatNumber(growthData.cumulative.products)}
@@ -161,7 +148,7 @@ export default function GrowthAnalyticsChart({ Avg Order Value
- {formatCurrency(growthData.cumulative.avgOrderValue)} + {hideNumbers ? "£***" : formatGBP(growthData.cumulative.avgOrderValue)}
@@ -236,16 +223,16 @@ export default function GrowthAnalyticsChart({ Orders:{" "} {hideNumbers ? "***" - : data.orders.toLocaleString()} + : formatNumber(data.orders)}

- Revenue: {formatCurrency(data.revenue)} + Revenue: {hideNumbers ? "£***" : formatGBP(data.revenue)}

Customers:{" "} {hideNumbers ? "***" - : data.customers.toLocaleString()} + : formatNumber(data.customers)}

{data.newCustomers !== undefined && (

@@ -327,16 +314,16 @@ export default function GrowthAnalyticsChart({ )} - {hideNumbers ? "***" : month.orders.toLocaleString()} + {hideNumbers ? "***" : formatNumber(month.orders)} - {formatCurrency(month.revenue)} + {hideNumbers ? "£***" : formatGBP(month.revenue)} - {hideNumbers ? "***" : month.customers.toLocaleString()} + {hideNumbers ? "***" : formatNumber(month.customers)} - {formatCurrency(month.avgOrderValue)} + {hideNumbers ? "£***" : formatGBP(month.avgOrderValue)} {hideNumbers ? "***" : (month.newCustomers ?? 0)} @@ -352,3 +339,5 @@ export default function GrowthAnalyticsChart({ ); } + + diff --git a/components/analytics/MetricsCard.tsx b/components/analytics/MetricsCard.tsx index 450870c..4374c22 100644 --- a/components/analytics/MetricsCard.tsx +++ b/components/analytics/MetricsCard.tsx @@ -1,6 +1,6 @@ "use client" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; import { TrendingUp, TrendingDown, Minus } from "lucide-react"; import { LucideIcon } from "lucide-react"; import { motion } from "framer-motion"; @@ -112,4 +112,4 @@ export default function MetricsCard({ ); -} \ No newline at end of file +} diff --git a/components/analytics/OrderAnalyticsChart.tsx b/components/analytics/OrderAnalyticsChart.tsx index fa01263..140cccb 100644 --- a/components/analytics/OrderAnalyticsChart.tsx +++ b/components/analytics/OrderAnalyticsChart.tsx @@ -1,13 +1,13 @@ "use client" import { useState, useEffect } from 'react'; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Badge } from "@/components/ui/badge"; -import { useToast } from "@/hooks/use-toast"; -import { Skeleton } from "@/components/ui/skeleton"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; +import { Badge } from "@/components/common/badge"; +import { useToast } from "@/lib/hooks/use-toast"; +import { Skeleton } from "@/components/common/skeleton"; import { BarChart3, Clock, CheckCircle, XCircle, AlertCircle, AlertTriangle } from "lucide-react"; import { getOrderAnalyticsWithStore, type OrderAnalytics } from "@/lib/services/analytics-service"; -import { formatGBP } from "@/utils/format"; +import { formatGBP } from "@/lib/utils/format"; import { ChartSkeleton } from './SkeletonLoaders'; interface OrderAnalyticsChartProps { @@ -201,4 +201,5 @@ export default function OrderAnalyticsChart({ timeRange }: OrderAnalyticsChartPr ); -} \ No newline at end of file +} + diff --git a/components/analytics/PredictionsChart.tsx b/components/analytics/PredictionsChart.tsx index 96e8954..7aa5522 100644 --- a/components/analytics/PredictionsChart.tsx +++ b/components/analytics/PredictionsChart.tsx @@ -7,16 +7,16 @@ import { CardDescription, CardHeader, CardTitle, -} from "@/components/ui/card"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; +} from "@/components/common/card"; +import { Badge } from "@/components/common/badge"; +import { Button } from "@/components/common/button"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from "@/components/ui/select"; +} from "@/components/common/select"; import { TrendingUp, TrendingDown, @@ -32,8 +32,8 @@ import { Info, Download, } from "lucide-react"; -import { useToast } from "@/hooks/use-toast"; -import { Skeleton } from "@/components/ui/skeleton"; +import { useToast } from "@/lib/hooks/use-toast"; +import { Skeleton } from "@/components/common/skeleton"; import CountUp from "react-countup"; import { getPredictionsOverviewWithStore, @@ -41,7 +41,7 @@ import { type PredictionsOverview, type StockPredictionsResponse, } from "@/lib/services/analytics-service"; -import { formatGBP } from "@/utils/format"; +import { formatGBP } from "@/lib/utils/format"; import { Table, TableBody, @@ -49,7 +49,7 @@ import { TableHead, TableHeader, TableRow, -} from "@/components/ui/table"; +} from "@/components/common/table"; import { format } from "date-fns"; import { AreaChart, @@ -65,9 +65,9 @@ import { TooltipContent, TooltipProvider, TooltipTrigger, -} from "@/components/ui/tooltip"; -import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; -import { Slider } from "@/components/ui/slider"; +} from "@/components/common/tooltip"; +import { Alert, AlertDescription, AlertTitle } from "@/components/common/alert"; +import { Slider } from "@/components/common/slider"; interface PredictionsChartProps { timeRange?: number; @@ -933,3 +933,5 @@ export default function PredictionsChart({ ); } + + diff --git a/components/analytics/ProductPerformanceChart.tsx b/components/analytics/ProductPerformanceChart.tsx index f7858c8..9b21adc 100644 --- a/components/analytics/ProductPerformanceChart.tsx +++ b/components/analytics/ProductPerformanceChart.tsx @@ -1,14 +1,14 @@ "use client" import { useState, useEffect } from 'react'; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; -import { Badge } from "@/components/ui/badge"; -import { useToast } from "@/hooks/use-toast"; -import { Skeleton } from "@/components/ui/skeleton"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/common/table"; +import { Badge } from "@/components/common/badge"; +import { useToast } from "@/lib/hooks/use-toast"; +import { Skeleton } from "@/components/common/skeleton"; import { Package } from "lucide-react"; import { getProductPerformanceWithStore, type ProductPerformance } from "@/lib/services/analytics-service"; -import { formatGBP } from "@/utils/format"; +import { formatGBP, formatNumber } from "@/lib/utils/format"; import { TableSkeleton } from './SkeletonLoaders'; export default function ProductPerformanceChart() { @@ -123,10 +123,10 @@ export default function ProductPerformanceChart() {

-
- {parseInt(product.totalSold.toFixed(0)).toLocaleString()} {product.unitType} + {formatNumber(parseInt(product.totalSold.toFixed(0)))} {product.unitType} {formatGBP(product.totalRevenue)} @@ -155,4 +155,5 @@ export default function ProductPerformanceChart() { ); -} \ No newline at end of file +} + diff --git a/components/analytics/ProfitAnalyticsChart.tsx b/components/analytics/ProfitAnalyticsChart.tsx index e2f3978..c4d237e 100644 --- a/components/analytics/ProfitAnalyticsChart.tsx +++ b/components/analytics/ProfitAnalyticsChart.tsx @@ -1,9 +1,9 @@ "use client" import { useState, useEffect } from 'react'; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Badge } from "@/components/ui/badge"; -import { Alert, AlertDescription } from "@/components/ui/alert"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; +import { Badge } from "@/components/common/badge"; +import { Alert, AlertDescription } from "@/components/common/alert"; import { TrendingUp, TrendingDown, @@ -14,10 +14,10 @@ import { AlertTriangle, Package } from "lucide-react"; -import { useToast } from "@/hooks/use-toast"; -import { formatGBP } from "@/utils/format"; +import { useToast } from "@/lib/hooks/use-toast"; +import { formatGBP } from "@/lib/utils/format"; import { getProfitOverview, type ProfitOverview, type DateRange } from "@/lib/services/profit-analytics-service"; -import { Skeleton } from "@/components/ui/skeleton"; +import { Skeleton } from "@/components/common/skeleton"; interface ProfitAnalyticsChartProps { timeRange?: string; @@ -379,3 +379,5 @@ export default function ProfitAnalyticsChart({ timeRange, dateRange, hideNumbers
); } + + diff --git a/components/analytics/RevenueChart.tsx b/components/analytics/RevenueChart.tsx index 7c27504..7879525 100644 --- a/components/analytics/RevenueChart.tsx +++ b/components/analytics/RevenueChart.tsx @@ -1,12 +1,12 @@ "use client" import { useState, useEffect } from 'react'; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { useToast } from "@/hooks/use-toast"; -import { Skeleton } from "@/components/ui/skeleton"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; +import { useToast } from "@/lib/hooks/use-toast"; +import { Skeleton } from "@/components/common/skeleton"; import { TrendingUp, DollarSign } from "lucide-react"; import { getRevenueTrendsWithStore, type RevenueData } from "@/lib/services/analytics-service"; -import { formatGBP } from "@/utils/format"; +import { formatGBP } from "@/lib/utils/format"; import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, AreaChart, Area } from 'recharts'; import { ChartSkeleton } from './SkeletonLoaders'; @@ -239,4 +239,5 @@ export default function RevenueChart({ timeRange, hideNumbers = false }: Revenue ); -} \ No newline at end of file +} + diff --git a/components/analytics/SkeletonLoaders.tsx b/components/analytics/SkeletonLoaders.tsx index 8415177..3ff81ff 100644 --- a/components/analytics/SkeletonLoaders.tsx +++ b/components/analytics/SkeletonLoaders.tsx @@ -1,5 +1,5 @@ -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; -import { Skeleton } from "@/components/ui/skeleton"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; +import { Skeleton } from "@/components/common/skeleton"; // Chart skeleton for revenue trends and order analytics export function ChartSkeleton({ @@ -167,4 +167,4 @@ export function MetricsCardSkeleton() { ); -} \ No newline at end of file +} diff --git a/components/analytics/StoreSelector.tsx b/components/analytics/StoreSelector.tsx index ebc9b5c..6a69926 100644 --- a/components/analytics/StoreSelector.tsx +++ b/components/analytics/StoreSelector.tsx @@ -2,12 +2,12 @@ import { useState, useEffect } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; +import { Label } from "@/components/common/label"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card"; import { Store, Search } from "lucide-react"; -import { useToast } from "@/hooks/use-toast"; +import { useToast } from "@/lib/hooks/use-toast"; export default function StoreSelector() { const [storeId, setStoreId] = useState(''); @@ -111,4 +111,5 @@ export default function StoreSelector() { ); -} \ No newline at end of file +} + diff --git a/components/animated-stats-section.tsx b/components/animated-stats-section.tsx index d84616d..8b71940 100644 --- a/components/animated-stats-section.tsx +++ b/components/animated-stats-section.tsx @@ -80,4 +80,4 @@ export function AnimatedStatsSection({ stats }: AnimatedStatsProps) {
); -} \ No newline at end of file +} diff --git a/components/ui/alert-dialog.tsx b/components/common/alert-dialog.tsx similarity index 98% rename from components/ui/alert-dialog.tsx rename to components/common/alert-dialog.tsx index 81f54b8..d472a6c 100644 --- a/components/ui/alert-dialog.tsx +++ b/components/common/alert-dialog.tsx @@ -4,7 +4,7 @@ import * as React from "react" import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog" import { cn } from "@/lib/utils/styles"; -import { buttonVariants } from "@/components/ui/button" +import { buttonVariants } from "@/components/common/button" const AlertDialog = AlertDialogPrimitive.Root @@ -139,3 +139,4 @@ export { AlertDialogAction, AlertDialogCancel, } + diff --git a/components/ui/alert.tsx b/components/common/alert.tsx similarity index 100% rename from components/ui/alert.tsx rename to components/common/alert.tsx diff --git a/components/ui/avatar.tsx b/components/common/avatar.tsx similarity index 100% rename from components/ui/avatar.tsx rename to components/common/avatar.tsx diff --git a/components/ui/badge.tsx b/components/common/badge.tsx similarity index 100% rename from components/ui/badge.tsx rename to components/common/badge.tsx diff --git a/components/ui/breadcrumb.tsx b/components/common/breadcrumb.tsx similarity index 100% rename from components/ui/breadcrumb.tsx rename to components/common/breadcrumb.tsx diff --git a/components/ui/button.tsx b/components/common/button.tsx similarity index 100% rename from components/ui/button.tsx rename to components/common/button.tsx diff --git a/components/ui/calendar.tsx b/components/common/calendar.tsx similarity index 97% rename from components/ui/calendar.tsx rename to components/common/calendar.tsx index 990feca..822b369 100644 --- a/components/ui/calendar.tsx +++ b/components/common/calendar.tsx @@ -5,7 +5,7 @@ import { ChevronLeft, ChevronRight } from "lucide-react" import { DayPicker } from "react-day-picker" import { cn } from "@/lib/utils/styles"; -import { buttonVariants } from "@/components/ui/button" +import { buttonVariants } from "@/components/common/button" export type CalendarProps = React.ComponentProps @@ -64,3 +64,4 @@ function Calendar({ Calendar.displayName = "Calendar" export { Calendar } + diff --git a/components/ui/card.tsx b/components/common/card.tsx similarity index 100% rename from components/ui/card.tsx rename to components/common/card.tsx diff --git a/components/ui/carousel.tsx b/components/common/carousel.tsx similarity index 99% rename from components/ui/carousel.tsx rename to components/common/carousel.tsx index 3bb8c21..165fade 100644 --- a/components/ui/carousel.tsx +++ b/components/common/carousel.tsx @@ -7,7 +7,7 @@ import useEmblaCarousel, { import { ArrowLeft, ArrowRight } from "lucide-react" import { cn } from "@/lib/utils/styles"; -import { Button } from "@/components/ui/button" +import { Button } from "@/components/common/button" type CarouselApi = UseEmblaCarouselType[1] type UseCarouselParameters = Parameters @@ -260,3 +260,4 @@ export { CarouselPrevious, CarouselNext, } + diff --git a/components/ui/chart.tsx b/components/common/chart.tsx similarity index 100% rename from components/ui/chart.tsx rename to components/common/chart.tsx diff --git a/components/ui/checkbox.tsx b/components/common/checkbox.tsx similarity index 100% rename from components/ui/checkbox.tsx rename to components/common/checkbox.tsx diff --git a/components/ui/collapsible.tsx b/components/common/collapsible.tsx similarity index 100% rename from components/ui/collapsible.tsx rename to components/common/collapsible.tsx diff --git a/components/ui/command.tsx b/components/common/command.tsx similarity index 69% rename from components/ui/command.tsx rename to components/common/command.tsx index 73d036c..3f6d92d 100644 --- a/components/ui/command.tsx +++ b/components/common/command.tsx @@ -6,7 +6,7 @@ import { Command as CommandPrimitive } from "cmdk" import { Search } from "lucide-react" import { cn } from "@/lib/utils/styles"; -import { Dialog, DialogContent } from "@/components/ui/dialog" +import { Dialog, DialogContent } from "@/components/common/dialog" const Command = React.forwardRef< React.ElementRef, @@ -15,7 +15,7 @@ const Command = React.forwardRef< { return ( - - + + {children} @@ -39,12 +39,12 @@ const CommandInput = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef >(({ className, ...props }, ref) => ( -
- +
+ (({ className, ...props }, ref) => ( )) @@ -115,7 +115,7 @@ const CommandItem = React.forwardRef<
) -} \ No newline at end of file +} diff --git a/components/ui/dialog.tsx b/components/common/dialog.tsx similarity index 100% rename from components/ui/dialog.tsx rename to components/common/dialog.tsx diff --git a/components/ui/drawer.tsx b/components/common/drawer.tsx similarity index 100% rename from components/ui/drawer.tsx rename to components/common/drawer.tsx diff --git a/components/ui/dropdown-menu.tsx b/components/common/dropdown-menu.tsx similarity index 100% rename from components/ui/dropdown-menu.tsx rename to components/common/dropdown-menu.tsx diff --git a/components/ui/empty-state.tsx b/components/common/empty-state.tsx similarity index 98% rename from components/ui/empty-state.tsx rename to components/common/empty-state.tsx index 81695d5..10785ae 100644 --- a/components/ui/empty-state.tsx +++ b/components/common/empty-state.tsx @@ -1,7 +1,7 @@ "use client" import * as React from "react" -import { Button } from "@/components/ui/button" +import { Button } from "@/components/common/button" import { Package, ShoppingBag, @@ -136,3 +136,4 @@ export function ChatsEmptyState() { /> ) } + diff --git a/components/ui/form.tsx b/components/common/form.tsx similarity index 98% rename from components/ui/form.tsx rename to components/common/form.tsx index 7b8d7e7..db3d21f 100644 --- a/components/ui/form.tsx +++ b/components/common/form.tsx @@ -13,7 +13,7 @@ import { } from "react-hook-form" import { cn } from "@/lib/utils/styles"; -import { Label } from "@/components/ui/label" +import { Label } from "@/components/common/label" const Form = FormProvider @@ -176,3 +176,4 @@ export { FormMessage, FormField, } + diff --git a/components/ui/input.tsx b/components/common/input.tsx similarity index 100% rename from components/ui/input.tsx rename to components/common/input.tsx diff --git a/components/ui/label.tsx b/components/common/label.tsx similarity index 100% rename from components/ui/label.tsx rename to components/common/label.tsx diff --git a/components/ui/menubar.tsx b/components/common/menubar.tsx similarity index 100% rename from components/ui/menubar.tsx rename to components/common/menubar.tsx diff --git a/components/ui/motion-wrapper.tsx b/components/common/motion-wrapper.tsx similarity index 100% rename from components/ui/motion-wrapper.tsx rename to components/common/motion-wrapper.tsx diff --git a/components/ui/navigation-menu.tsx b/components/common/navigation-menu.tsx similarity index 100% rename from components/ui/navigation-menu.tsx rename to components/common/navigation-menu.tsx diff --git a/components/ui/pagination.tsx b/components/common/pagination.tsx similarity index 97% rename from components/ui/pagination.tsx rename to components/common/pagination.tsx index 9ad1c59..d0691f8 100644 --- a/components/ui/pagination.tsx +++ b/components/common/pagination.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { ChevronLeft, ChevronRight, MoreHorizontal } from "lucide-react" import { cn } from "@/lib/utils/styles"; -import { ButtonProps, buttonVariants } from "@/components/ui/button" +import { ButtonProps, buttonVariants } from "@/components/common/button" const Pagination = ({ className, ...props }: React.ComponentProps<"nav">) => (
); -} \ No newline at end of file +} + diff --git a/components/dashboard/ChatTable.tsx b/components/dashboard/ChatTable.tsx index 80d7c08..162edf8 100644 --- a/components/dashboard/ChatTable.tsx +++ b/components/dashboard/ChatTable.tsx @@ -10,11 +10,11 @@ import { TableHead, TableHeader, TableRow, -} from "@/components/ui/table"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; -import { Avatar, AvatarFallback } from "@/components/ui/avatar"; +} from "@/components/common/table"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/common/card"; +import { Badge } from "@/components/common/badge"; +import { Button } from "@/components/common/button"; +import { Avatar, AvatarFallback } from "@/components/common/avatar"; import { formatDistanceToNow } from "date-fns"; import { Plus, @@ -41,7 +41,7 @@ import { SelectItem, SelectTrigger, SelectValue, -} from "@/components/ui/select"; +} from "@/components/common/select"; import { toast } from "sonner"; import { clientFetch, getCookie } from "@/lib/api"; import { formatDistance } from 'date-fns'; @@ -474,3 +474,5 @@ export default function ChatTable() { ); } + + diff --git a/components/dashboard/NewChatForm.tsx b/components/dashboard/NewChatForm.tsx index eb5f019..cc6c2d6 100644 --- a/components/dashboard/NewChatForm.tsx +++ b/components/dashboard/NewChatForm.tsx @@ -8,11 +8,11 @@ import { CardHeader, CardTitle, CardDescription, -} from "@/components/ui/card"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Textarea } from "@/components/ui/textarea"; +} from "@/components/common/card"; +import { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; +import { Label } from "@/components/common/label"; +import { Textarea } from "@/components/common/textarea"; import { ArrowLeft, Send, RefreshCw, Search, User } from "lucide-react"; import axios from "axios"; import { toast } from "sonner"; @@ -376,3 +376,5 @@ export default function NewChatForm() { ); } + + diff --git a/components/dashboard/command-palette.tsx b/components/dashboard/command-palette.tsx new file mode 100644 index 0000000..15874bf --- /dev/null +++ b/components/dashboard/command-palette.tsx @@ -0,0 +1,139 @@ +"use client" + +import * as React from "react" +import { + Calculator, + Calendar, + CreditCard, + Settings, + Smile, + User, + LayoutDashboard, + Package, + ShoppingCart, + Users, + BarChart3, + RefreshCcw, + RotateCcw, + Search, + MessageSquare, + Truck +} from "lucide-react" + +import { + CommandDialog, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + CommandSeparator, + CommandShortcut, +} from "@/components/common/command" +import { useRouter } from "next/navigation" + +interface CommandPaletteProps { + onResetLayout?: () => void + onToggleWidget?: (id: string) => void + availableWidgets?: Array<{ id: string; title: string; visible: boolean }> +} + +export function CommandPalette({ onResetLayout, onToggleWidget, availableWidgets }: CommandPaletteProps) { + const [open, setOpen] = React.useState(false) + const router = useRouter() + + React.useEffect(() => { + const down = (e: KeyboardEvent) => { + if (e.key === "k" && (e.metaKey || e.ctrlKey)) { + e.preventDefault() + setOpen((open) => !open) + } + } + + document.addEventListener("keydown", down) + return () => document.removeEventListener("keydown", down) + }, []) + + const runCommand = React.useCallback((command: () => void) => { + setOpen(false) + command() + }, []) + + return ( + + + + No results found. + + runCommand(() => router.push("/dashboard"))}> + + Dashboard + + runCommand(() => router.push("/dashboard/orders"))}> + + Manage Orders + + runCommand(() => router.push("/dashboard/stock"))}> + + Inventory & Stock + + runCommand(() => router.push("/dashboard/customers"))}> + + Customers + + runCommand(() => router.push("/dashboard/analytics"))}> + + Advanced Analytics + + runCommand(() => router.push("/dashboard/shipping"))}> + + Shipping Options + + + + + + + runCommand(() => window.location.reload())}> + + Reload Data + + {onResetLayout && ( + runCommand(onResetLayout)}> + + Reset Widget Layout + + )} + + + {availableWidgets && availableWidgets.length > 0 && ( + <> + + + {availableWidgets.map((widget) => ( + runCommand(() => onToggleWidget?.(widget.id))} + > +
+ {widget.visible ? "Hide" : "Show"} {widget.title} + + ))} + + + )} + + + + + runCommand(() => router.push("/dashboard/settings"))}> + + General Settings + ⌘S + + + + + ) +} + diff --git a/components/dashboard/content.tsx b/components/dashboard/content.tsx index 9f4d906..b24dc9d 100644 --- a/components/dashboard/content.tsx +++ b/components/dashboard/content.tsx @@ -8,43 +8,47 @@ import { WidgetSettings } from "./widget-settings" import { WidgetSettingsModal } from "./widget-settings-modal" import { DashboardEditor } from "./dashboard-editor" import { DraggableWidget } from "./draggable-widget" +import { CommandPalette } from "./command-palette" import RevenueWidget from "./revenue-widget" import LowStockWidget from "./low-stock-widget" import RecentCustomersWidget from "./recent-customers-widget" +import { ProductPeek } from "./product-peek" import PendingChatsWidget from "./pending-chats-widget" import { getGreeting } from "@/lib/utils/general" import { statsConfig } from "@/config/dashboard" import { getRandomQuote } from "@/config/quotes" import type { OrderStatsData } from "@/lib/types" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card" import { ShoppingCart, RefreshCcw, ArrowRight } from "lucide-react" -import { Button } from "@/components/ui/button" -import { useToast } from "@/components/ui/use-toast" -import { Skeleton } from "@/components/ui/skeleton" +import { Button } from "@/components/common/button" +import { useToast } from "@/components/common/use-toast" +import { Skeleton } from "@/components/common/skeleton" import { clientFetch } from "@/lib/api" import { motion } from "framer-motion" import Link from "next/link" -import { useWidgetLayout, WidgetConfig } from "@/hooks/useWidgetLayout" +import { useWidgetLayout } from "@/lib/hooks/useWidgetLayout" +import type { TopProduct, WidgetConfig } from "@/lib/types/dashboard" interface ContentProps { username: string orderStats: OrderStatsData } -interface TopProduct { - id: string; - name: string; - price: number | number[]; - image: string; - count: number; - revenue: number; -} +// TopProduct interface moved to @/lib/types/dashboard export default function Content({ username, orderStats }: ContentProps) { const [greeting, setGreeting] = useState(""); const [topProducts, setTopProducts] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); + const [loading, setLoading] = useState(true); + const [errorQuery, setErrorQuery] = useState(false); + const [selectedProductId, setSelectedProductId] = useState(null); + const [isProductPeekOpen, setIsProductPeekOpen] = useState(false); + const [trends, setTrends] = useState([]); + + const handleProductPeek = (id: string) => { + setSelectedProductId(id); + setIsProductPeekOpen(true); + }; const { toast } = useToast(); const { widgets, toggleWidget, moveWidget, reorderWidgets, resetLayout, isWidgetVisible, updateWidgetSettings } = useWidgetLayout(); const [configuredWidget, setConfiguredWidget] = useState(null); @@ -59,14 +63,25 @@ export default function Content({ username, orderStats }: ContentProps) { const fetchTopProducts = async () => { try { - setIsLoading(true); + setLoading(true); const data = await clientFetch('/orders/top-products'); setTopProducts(data); } catch (err) { console.error("Error fetching top products:", err); - setError(err instanceof Error ? err.message : "Failed to fetch top products"); + setErrorQuery(true); } finally { - setIsLoading(false); + setLoading(false); + } + }; + + const fetchTrends = async () => { + try { + const data = await clientFetch('/analytics/revenue-trends?period=30'); + if (Array.isArray(data)) { + setTrends(data); + } + } catch (err) { + console.error("Error fetching trends:", err); } }; @@ -88,16 +103,35 @@ export default function Content({ username, orderStats }: ContentProps) {

Overview

- {statsConfig.map((stat, index) => ( - - ))} + {statsConfig.map((stat, index) => { + const colors = ["#8884d8", "#10b981", "#3b82f6", "#f59e0b"]; + const val = Number(orderStats[stat.key as keyof OrderStatsData]) || 0; + + // Map real trend data if available, otherwise fallback to empty (or subtle random if just started) + let trendData = trends.map(t => ({ + value: stat.key === "revenue" ? t.revenue : t.orders + })).slice(-12); // Last 12 points + + // Fallback for demo/new stores if no trends yet + if (trendData.length === 0) { + trendData = Array.from({ length: 12 }, (_, i) => ({ + value: Math.max(0, val * (0.8 + Math.random() * 0.4 + (i / 20))) + })); + } + + return ( + + ); + })}
); @@ -118,7 +152,7 @@ export default function Content({ username, orderStats }: ContentProps) { Top Performing Listings Your products with the highest sales volume
- {error && ( + {errorQuery && ( + + )} + + +
+ {loading ? : order ? `Order #${order.orderId}` : "Loading..."} +
+
+ + + + {loading ? ( +
+
+ + +
+ +
+ {[1, 2, 3].map(i => )} +
+
+ ) : order ? ( +
+ {/* Summary Stats */} +
+
+ Status +
+ + {order.status} + +
+
+
+ Total Value +
+ {formatGBP(order.totalPrice)} +
+
+
+ + {/* Customer Info */} +
+

+ Customer Information +

+
+
+ Customer +
+ + {order.telegramUsername ? `@${order.telegramUsername}` : "Anonymous"} + + + ID: {order.telegramBuyerId?.slice(0, 16) || "N/A"} + +
+
+
+ Order Date + + {order.orderDate ? : "N/A"} + +
+
+
+ + {/* Order Items */} +
+

+ Order Items +

+
+ {order.products?.map((item: any, idx: number) => ( +
+
+ + {item.name || `Item ${idx + 1}`} + + Qty: {item.quantity} +
+ {formatGBP(item.totalItemPrice)} +
+ ))} + {order.shippingMethod && ( +
+
+ Shipping: {order.shippingMethod.type} + Standard Delivery +
+ {formatGBP(order.shippingMethod.price)} +
+ )} +
+
+ + {/* Shipping Address Peek */} +
+

+ Delivery Address +

+
+

+ {order.pgpAddress || "No address provided or encrypted."} +

+
+
+ + {/* Timeline Indicator */} +
+

+ Next Milestone +

+
+
+
+ +
+
+

Pending Shipment

+

Awaiting vendor processing and label creation.

+
+
+
+
+
+ ) : ( +
+ Failed to load order data. +
+ )} +
+ + {order && ( +
+ onOpenChange(false)}> + + +
+ )} + + + ) +} + + diff --git a/components/dashboard/order-stats.tsx b/components/dashboard/order-stats.tsx index abe454b..43b3c6f 100644 --- a/components/dashboard/order-stats.tsx +++ b/components/dashboard/order-stats.tsx @@ -1,7 +1,8 @@ import type { LucideIcon } from "lucide-react" -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/common/card" import { motion } from "framer-motion" import Link from "next/link" +import { ResponsiveContainer, LineChart, Line } from "recharts" interface OrderStatsProps { title: string @@ -12,6 +13,10 @@ interface OrderStatsProps { filterStatus?: string /** Custom href if not using filterStatus */ href?: string + /** Data for sparkline [ { value: number }, ... ] */ + trendData?: { value: number }[] + /** Color for the sparkline */ + trendColor?: string } export default function OrderStats({ @@ -20,7 +25,9 @@ export default function OrderStats({ icon: Icon, index = 0, filterStatus, - href + href, + trendData, + trendColor = "#8884d8" }: OrderStatsProps) { const linkHref = href || (filterStatus ? `/dashboard/orders?status=${filterStatus}` : undefined) @@ -45,8 +52,26 @@ export default function OrderStats({ -
{value}
-
+
+
{value}
+ {trendData && trendData.length > 0 && ( +
+ + + + + +
+ )} +
+
{linkHref && (
Click to view → @@ -59,3 +84,4 @@ export default function OrderStats({ ) } + diff --git a/components/dashboard/page-loading.tsx b/components/dashboard/page-loading.tsx index 5574836..2ec88f6 100644 --- a/components/dashboard/page-loading.tsx +++ b/components/dashboard/page-loading.tsx @@ -1,7 +1,7 @@ "use client" -import { Skeleton } from "@/components/ui/skeleton"; -import { Card } from "@/components/ui/card"; +import { Skeleton } from "@/components/common/skeleton"; +import { Card } from "@/components/common/card"; import { Loader2 } from "lucide-react"; import { SnowLoader } from "@/components/snow-loader"; @@ -113,4 +113,4 @@ export default function PageLoading({
); -} \ No newline at end of file +} diff --git a/components/dashboard/pending-chats-widget.tsx b/components/dashboard/pending-chats-widget.tsx index e56567d..48eb7fe 100644 --- a/components/dashboard/pending-chats-widget.tsx +++ b/components/dashboard/pending-chats-widget.tsx @@ -1,14 +1,14 @@ "use client" import { useState, useEffect } from "react" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" -import { Button } from "@/components/ui/button" -import { Skeleton } from "@/components/ui/skeleton" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card" +import { Button } from "@/components/common/button" +import { Skeleton } from "@/components/common/skeleton" import { MessageSquare, MessageCircle, ArrowRight, Clock } from "lucide-react" import { clientFetch, getCookie } from "@/lib/api" -import { Avatar, AvatarFallback } from "@/components/ui/avatar" +import { Avatar, AvatarFallback } from "@/components/common/avatar" import Link from "next/link" -import { RelativeTime } from "@/components/ui/relative-time" +import { RelativeTime } from "@/components/common/relative-time" interface PendingChatsWidgetProps { settings?: { @@ -170,3 +170,5 @@ export default function PendingChatsWidget({ settings }: PendingChatsWidgetProps ) } + + diff --git a/components/dashboard/product-peek.tsx b/components/dashboard/product-peek.tsx new file mode 100644 index 0000000..88eab03 --- /dev/null +++ b/components/dashboard/product-peek.tsx @@ -0,0 +1,191 @@ +"use client" + +import { useState, useEffect } from "react" +import { + Sheet, + SheetContent, + SheetHeader, + SheetTitle, + SheetDescription +} from "@/components/common/sheet" +import { clientFetch } from "@/lib/api" +import { Badge } from "@/components/common/badge" +import { Separator } from "@/components/common/separator" +import { ScrollArea } from "@/components/common/scroll-area" +import { ShoppingCart, TrendingUp, BarChart3, Package, History, ExternalLink, AlertCircle } from "lucide-react" +import { formatGBP } from "@/lib/utils/format" +import Link from "next/link" +import { Button } from "@/components/common/button" +import { Skeleton } from "@/components/common/skeleton" + +interface ProductPeekProps { + productId: string | null + open: boolean + onOpenChange: (open: boolean) => void +} + +export function ProductPeek({ productId, open, onOpenChange }: ProductPeekProps) { + const [product, setProduct] = useState(null) + const [loading, setLoading] = useState(false) + + useEffect(() => { + if (open && productId) { + fetchProductDetails() + } + }, [open, productId]) + + const fetchProductDetails = async () => { + setLoading(true) + try { + // In this app, productId might be the ID used in the list, + // which corresponds to the mongo _id or a semantic ID. + const data = await clientFetch(`/products/${productId}`) + setProduct(data) + } catch (error) { + console.error("Failed to fetch product details for peek:", error) + } finally { + setLoading(false) + } + } + + return ( + + + +
+ + + Product Insight + + {product && ( + onOpenChange(false)}> + + + )} +
+ +
+ {loading ? : product ? product.name : "Loading..."} +
+
+
+ + + {loading ? ( +
+
+ +
+ + +
+
+ +
+ + +
+
+ ) : product ? ( +
+ {/* Product Visual & Basic Info */} +
+
+
+ + {product.type || "Physical"} + +

{product.name}

+
+
+ + {/* Inventory Stats */} +
+
+ Current Stock +
+ + {product.currentStock || 0} + + {(product.currentStock || 0) <= 5 && } +
+
+
+ Unit Type +
+ {product.unitType || "Units"} +
+
+
+ + {/* Analytics Section */} +
+

+ Performance Metrics +

+
+
+ Type + {product.type || "Physical"} +
+
+ Rating +
+
+ {"★".repeat(Math.round(product.rating || 5))} +
+ ({product.rating || "5.0"}) +
+
+
+
+ + {/* Status & Settings */} +
+

+ Market Visibility +

+
+
+
+ +
+
+

Active Listing

+

This product is currently visible to all buyers.

+
+
+
+
+ + {/* Quick Actions */} +
+ onOpenChange(false)} className="w-full"> + + +
+
+ ) : ( +
+ Failed to load product details. +
+ )} + + + + ) +} + + diff --git a/components/dashboard/promotions/EditPromotionForm.tsx b/components/dashboard/promotions/EditPromotionForm.tsx index 473b117..d21b40b 100644 --- a/components/dashboard/promotions/EditPromotionForm.tsx +++ b/components/dashboard/promotions/EditPromotionForm.tsx @@ -5,7 +5,7 @@ import { z } from 'zod'; import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import { Save, X, Loader2 } from 'lucide-react'; -import { Button } from '@/components/ui/button'; +import { Button } from '@/components/common/button'; import { Form, FormControl, @@ -14,12 +14,12 @@ import { FormItem, FormLabel, FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; -import { Switch } from '@/components/ui/switch'; -import { Textarea } from '@/components/ui/textarea'; -import { toast } from '@/components/ui/use-toast'; +} from '@/components/common/form'; +import { Input } from '@/components/common/input'; +import { RadioGroup, RadioGroupItem } from "@/components/common/radio-group"; +import { Switch } from '@/components/common/switch'; +import { Textarea } from '@/components/common/textarea'; +import { toast } from '@/components/common/use-toast'; import { Promotion, PromotionFormData } from '@/lib/types/promotion'; import { fetchClient } from '@/lib/api'; import dynamic from 'next/dynamic'; @@ -464,4 +464,5 @@ export default function EditPromotionForm({ promotion, onSuccess, onCancel }: Ed ); -} \ No newline at end of file +} + diff --git a/components/dashboard/promotions/NewPromotionForm.tsx b/components/dashboard/promotions/NewPromotionForm.tsx index 715799c..e569b37 100644 --- a/components/dashboard/promotions/NewPromotionForm.tsx +++ b/components/dashboard/promotions/NewPromotionForm.tsx @@ -5,7 +5,7 @@ import { z } from 'zod'; import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; import { Plus, Save, X, Loader2 } from 'lucide-react'; -import { Button } from '@/components/ui/button'; +import { Button } from '@/components/common/button'; import { Form, FormControl, @@ -14,15 +14,15 @@ import { FormItem, FormLabel, FormMessage, -} from '@/components/ui/form'; -import { Input } from '@/components/ui/input'; -import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; -import { Switch } from '@/components/ui/switch'; -import { Textarea } from '@/components/ui/textarea'; -import { toast } from '@/components/ui/use-toast'; +} from '@/components/common/form'; +import { Input } from '@/components/common/input'; +import { RadioGroup, RadioGroupItem } from "@/components/common/radio-group"; +import { Switch } from '@/components/common/switch'; +import { Textarea } from '@/components/common/textarea'; +import { toast } from '@/components/common/use-toast'; import { PromotionFormData } from '@/lib/types/promotion'; import { fetchClient } from '@/lib/api'; -import { DatePicker } from '@/components/ui/date-picker'; +import { DatePicker } from '@/components/common/date-picker'; import dynamic from 'next/dynamic'; const ProductSelector = dynamic(() => import('./ProductSelector')); @@ -459,4 +459,5 @@ export default function NewPromotionForm({ onSuccess, onCancel }: NewPromotionFo ); -} \ No newline at end of file +} + diff --git a/components/dashboard/promotions/ProductSelector.tsx b/components/dashboard/promotions/ProductSelector.tsx index 27bc2b2..4d930d2 100644 --- a/components/dashboard/promotions/ProductSelector.tsx +++ b/components/dashboard/promotions/ProductSelector.tsx @@ -2,12 +2,12 @@ import { useState, useEffect, useRef } from 'react'; import { Check, ChevronDown, X, Search } from 'lucide-react'; -import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; -import { Badge } from '@/components/ui/badge'; -import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; -import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from '@/components/ui/command'; -import { ScrollArea } from '@/components/ui/scroll-area'; +import { Button } from '@/components/common/button'; +import { Input } from '@/components/common/input'; +import { Badge } from '@/components/common/badge'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/common/popover'; +import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from '@/components/common/command'; +import { ScrollArea } from '@/components/common/scroll-area'; import { Product } from '@/lib/types/promotion'; import { fetchClient } from '@/lib/api'; @@ -248,4 +248,5 @@ export default function ProductSelector({ )}
); -} \ No newline at end of file +} + diff --git a/components/dashboard/promotions/PromotionDetailsModal.tsx b/components/dashboard/promotions/PromotionDetailsModal.tsx index 8ed342c..005d238 100644 --- a/components/dashboard/promotions/PromotionDetailsModal.tsx +++ b/components/dashboard/promotions/PromotionDetailsModal.tsx @@ -7,11 +7,11 @@ import { DialogHeader, DialogTitle, DialogDescription -} from '@/components/ui/dialog'; -import { Badge } from '@/components/ui/badge'; -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { Separator } from '@/components/ui/separator'; -import { ScrollArea } from '@/components/ui/scroll-area'; +} from '@/components/common/dialog'; +import { Badge } from '@/components/common/badge'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/common/card'; +import { Separator } from '@/components/common/separator'; +import { ScrollArea } from '@/components/common/scroll-area'; import { Calendar, Target, @@ -28,7 +28,7 @@ import { } from 'lucide-react'; import { Promotion, Product } from '@/lib/types/promotion'; import { fetchClient } from '@/lib/api'; -import { toast } from '@/components/ui/use-toast'; +import { toast } from '@/components/common/use-toast'; interface PromotionDetailsModalProps { promotion: Promotion | null; @@ -382,4 +382,5 @@ export default function PromotionDetailsModal({
); -} \ No newline at end of file +} + diff --git a/components/dashboard/promotions/PromotionsList.tsx b/components/dashboard/promotions/PromotionsList.tsx index 0e4daa2..e29e314 100644 --- a/components/dashboard/promotions/PromotionsList.tsx +++ b/components/dashboard/promotions/PromotionsList.tsx @@ -3,7 +3,7 @@ import { useState, useEffect } from 'react'; import { Plus, Tag, RefreshCw, Trash, Edit, Check, X, Eye } from 'lucide-react'; import { useRouter } from 'next/navigation'; -import { Button } from '@/components/ui/button'; +import { Button } from '@/components/common/button'; import { Table, TableBody, @@ -11,14 +11,14 @@ import { TableHead, TableHeader, TableRow, -} from '@/components/ui/table'; +} from '@/components/common/table'; import { Card, CardContent, CardDescription, CardHeader, CardTitle, -} from '@/components/ui/card'; +} from '@/components/common/card'; import { Dialog, DialogContent, @@ -27,9 +27,9 @@ import { DialogHeader, DialogTitle, DialogTrigger, -} from '@/components/ui/dialog'; -import { toast } from '@/components/ui/use-toast'; -import { Badge } from '@/components/ui/badge'; +} from '@/components/common/dialog'; +import { toast } from '@/components/common/use-toast'; +import { Badge } from '@/components/common/badge'; import { Promotion } from '@/lib/types/promotion'; import { fetchClient } from '@/lib/api'; import dynamic from 'next/dynamic'; @@ -306,4 +306,5 @@ export default function PromotionsList() { /> ); -} \ No newline at end of file +} + diff --git a/components/dashboard/promotions/PromotionsPageSkeleton.tsx b/components/dashboard/promotions/PromotionsPageSkeleton.tsx index af97572..cee6e93 100644 --- a/components/dashboard/promotions/PromotionsPageSkeleton.tsx +++ b/components/dashboard/promotions/PromotionsPageSkeleton.tsx @@ -5,9 +5,9 @@ import { TableHead, TableHeader, TableRow, -} from '@/components/ui/table'; -import { Card, CardContent } from '@/components/ui/card'; -import { Skeleton } from '@/components/ui/skeleton'; +} from '@/components/common/table'; +import { Card, CardContent } from '@/components/common/card'; +import { Skeleton } from '@/components/common/skeleton'; export default function PromotionsPageSkeleton() { return ( @@ -59,4 +59,4 @@ export default function PromotionsPageSkeleton() { ); -} \ No newline at end of file +} diff --git a/components/dashboard/quick-actions.tsx b/components/dashboard/quick-actions.tsx index 5a02f90..ecb5379 100644 --- a/components/dashboard/quick-actions.tsx +++ b/components/dashboard/quick-actions.tsx @@ -9,10 +9,10 @@ import { BarChart3, MessageSquare, } from "lucide-react" -import { Card, CardContent } from "@/components/ui/card" +import { Card, CardContent } from "@/components/common/card" import dynamic from "next/dynamic" -import { Product } from "@/models/products" -import { Category } from "@/models/categories" +import { Product } from "@/lib/models/products" +import { Category } from "@/lib/models/categories" import { clientFetch } from "@/lib/api" import { toast } from "sonner" @@ -217,3 +217,5 @@ export default function QuickActions() { ) } + + diff --git a/components/dashboard/recent-activity.tsx b/components/dashboard/recent-activity.tsx index 8c9cfc8..68ac642 100644 --- a/components/dashboard/recent-activity.tsx +++ b/components/dashboard/recent-activity.tsx @@ -3,11 +3,12 @@ import { useState, useEffect } from "react" import { motion } from "framer-motion" import { ShoppingBag, CreditCard, Truck, MessageSquare, AlertCircle } from "lucide-react" -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card" +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/common/card" import { clientFetch } from "@/lib/api" -import { Skeleton } from "@/components/ui/skeleton" -import { RelativeTime } from "@/components/ui/relative-time" +import { Skeleton } from "@/components/common/skeleton" +import { RelativeTime } from "@/components/common/relative-time" import Link from "next/link" +import { OrderPeek } from "./order-peek" interface ActivityItem { _id: string; @@ -21,6 +22,13 @@ interface ActivityItem { export default function RecentActivity() { const [activities, setActivities] = useState([]); const [loading, setLoading] = useState(true); + const [selectedOrderId, setSelectedOrderId] = useState(null); + const [isPeekOpen, setIsPeekOpen] = useState(false); + + const handlePeek = (id: string) => { + setSelectedOrderId(id); + setIsPeekOpen(true); + }; useEffect(() => { async function fetchRecentOrders() { @@ -89,16 +97,19 @@ export default function RecentActivity() { className="flex items-start gap-4 relative" > {index !== activities.length - 1 && ( -
+
)}
{getStatusIcon(item.status)}
- + @@ -106,14 +117,21 @@ export default function RecentActivity() {

{item.status === "paid" ? "Payment received" : item.status === "shipped" ? "Order marked as shipped" : - `Order status: ${item.status}`} for £{item.totalPrice.toFixed(2)} + `Order status: ${item.status}`}

))}
)} + ) } + + diff --git a/components/dashboard/recent-customers-widget.tsx b/components/dashboard/recent-customers-widget.tsx index 19f9612..63c8982 100644 --- a/components/dashboard/recent-customers-widget.tsx +++ b/components/dashboard/recent-customers-widget.tsx @@ -1,12 +1,12 @@ "use client" import { useState, useEffect } from "react" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" -import { Button } from "@/components/ui/button" -import { Skeleton } from "@/components/ui/skeleton" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card" +import { Button } from "@/components/common/button" +import { Skeleton } from "@/components/common/skeleton" import { Users, User, ArrowRight, DollarSign } from "lucide-react" import { getCustomerInsightsWithStore, formatGBP } from "@/lib/api" -import { Avatar, AvatarFallback } from "@/components/ui/avatar" +import { Avatar, AvatarFallback } from "@/components/common/avatar" import Link from "next/link" interface RecentCustomersWidgetProps { @@ -152,3 +152,5 @@ export default function RecentCustomersWidget({ settings }: RecentCustomersWidge ) } + + diff --git a/components/dashboard/revenue-widget.tsx b/components/dashboard/revenue-widget.tsx index a55a330..2e9691f 100644 --- a/components/dashboard/revenue-widget.tsx +++ b/components/dashboard/revenue-widget.tsx @@ -1,13 +1,13 @@ "use client" import { useState, useEffect } from "react" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" -import { Button } from "@/components/ui/button" -import { Skeleton } from "@/components/ui/skeleton" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/common/card" +import { Button } from "@/components/common/button" +import { Skeleton } from "@/components/common/skeleton" import { TrendingUp, DollarSign, RefreshCcw } from "lucide-react" import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from "recharts" import { getRevenueTrendsWithStore, type RevenueData, formatGBP } from "@/lib/api" -import { useToast } from "@/components/ui/use-toast" +import { useToast } from "@/components/common/use-toast" interface RevenueWidgetProps { settings?: { @@ -63,7 +63,6 @@ export default function RevenueWidget({ settings }: RevenueWidgetProps) { }) // Summary stats - const totalRevenue = data.reduce((sum, item) => sum + (item.revenue || 0), 0) const totalOrders = data.reduce((sum, item) => sum + (item.orders || 0), 0) const CustomTooltip = ({ active, payload }: any) => { @@ -74,9 +73,6 @@ export default function RevenueWidget({ settings }: RevenueWidgetProps) {

{data.formattedDate}

- Revenue: {formatGBP(data.revenue)} -

-

Orders: {data.orders}

@@ -106,7 +102,7 @@ export default function RevenueWidget({ settings }: RevenueWidgetProps) {
- Revenue Insights + Order Insights Performance over the last {days} days @@ -122,15 +118,15 @@ export default function RevenueWidget({ settings }: RevenueWidgetProps) { {error ? (
-

Could not load revenue trends

+

Could not load order trends

) : chartData.length === 0 ? (
-

No revenue data

+

No order data

- Start making sales to see your revenue trends here. + Start making sales to see your order trends here.

) : ( @@ -139,7 +135,7 @@ export default function RevenueWidget({ settings }: RevenueWidgetProps) { - + @@ -156,15 +152,15 @@ export default function RevenueWidget({ settings }: RevenueWidgetProps) { tick={{ fontSize: 11, fill: "hsl(var(--muted-foreground))" }} axisLine={false} tickLine={false} - tickFormatter={(value) => `£${value >= 1000 ? (value / 1000).toFixed(1) + 'k' : value}`} + tickFormatter={(value) => `${value}`} /> } /> @@ -172,14 +168,10 @@ export default function RevenueWidget({ settings }: RevenueWidgetProps) {
-
-
-
Total Revenue
-
{formatGBP(totalRevenue)}
-
-
+
+
Total Orders
-
{totalOrders}
+
{totalOrders}
@@ -188,3 +180,5 @@ export default function RevenueWidget({ settings }: RevenueWidgetProps) { ) } + + diff --git a/components/dashboard/widget-settings-modal.tsx b/components/dashboard/widget-settings-modal.tsx index 37b645e..3f161a0 100644 --- a/components/dashboard/widget-settings-modal.tsx +++ b/components/dashboard/widget-settings-modal.tsx @@ -1,7 +1,7 @@ "use client" import { useState } from "react" -import { Button } from "@/components/ui/button" +import { Button } from "@/components/common/button" import { Dialog, DialogContent, @@ -9,20 +9,20 @@ import { DialogTitle, DialogDescription, DialogFooter, -} from "@/components/ui/dialog" -import { Label } from "@/components/ui/label" -import { Input } from "@/components/ui/input" -import { Switch } from "@/components/ui/switch" +} from "@/components/common/dialog" +import { Label } from "@/components/common/label" +import { Input } from "@/components/common/input" +import { Switch } from "@/components/common/switch" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from "@/components/ui/select" -import { WidgetConfig } from "@/hooks/useWidgetLayout" +} from "@/components/common/select" +import { WidgetConfig } from "@/lib/types/dashboard" import { Settings2 } from "lucide-react" -import { ScrollArea } from "@/components/ui/scroll-area" +import { ScrollArea } from "@/components/common/scroll-area" interface WidgetSettingsModalProps { widget: WidgetConfig | null @@ -269,3 +269,5 @@ export function WidgetSettingsModal({ widget, open, onOpenChange, onSave }: Widg ) } + + diff --git a/components/dashboard/widget-settings.tsx b/components/dashboard/widget-settings.tsx index d21ffd4..46b83d9 100644 --- a/components/dashboard/widget-settings.tsx +++ b/components/dashboard/widget-settings.tsx @@ -1,6 +1,6 @@ "use client" -import { Button } from "@/components/ui/button" +import { Button } from "@/components/common/button" import { DropdownMenu, DropdownMenuContent, @@ -9,9 +9,9 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, DropdownMenuCheckboxItem, -} from "@/components/ui/dropdown-menu" +} from "@/components/common/dropdown-menu" import { Settings2, ChevronUp, ChevronDown, RotateCcw, Eye, EyeOff, Cog } from "lucide-react" -import { WidgetConfig } from "@/hooks/useWidgetLayout" +import { WidgetConfig } from "@/lib/types/dashboard" interface WidgetSettingsProps { widgets: WidgetConfig[] @@ -99,3 +99,5 @@ export function WidgetSettings({ widgets, onToggle, onMove, onReset, onConfigure ) } + + diff --git a/components/forms/image-upload.tsx b/components/forms/image-upload.tsx index 6e4cfab..5cb89f8 100644 --- a/components/forms/image-upload.tsx +++ b/components/forms/image-upload.tsx @@ -1,7 +1,7 @@ "use client"; import { ChangeEvent } from "react"; -import { Input } from "@/components/ui/input"; +import { Input } from "@/components/common/input"; import Image from "next/image"; interface ImageUploadProps { @@ -44,4 +44,4 @@ export const ImageUpload = ({ className="mt-2 h-9 text-sm" />
-); \ No newline at end of file +); diff --git a/components/forms/pricing-tiers.tsx b/components/forms/pricing-tiers.tsx index a1d3228..20fee20 100644 --- a/components/forms/pricing-tiers.tsx +++ b/components/forms/pricing-tiers.tsx @@ -1,7 +1,7 @@ "use client"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; +import { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; import { Trash, PlusCircle } from "lucide-react"; interface PricingTiersProps { @@ -137,3 +137,4 @@ export const PricingTiers = ({
); }; + diff --git a/components/home-navbar.tsx b/components/layout/home-navbar.tsx similarity index 98% rename from components/home-navbar.tsx rename to components/layout/home-navbar.tsx index 1cd145c..e783b22 100644 --- a/components/home-navbar.tsx +++ b/components/layout/home-navbar.tsx @@ -1,7 +1,7 @@ "use client"; import Link from "next/link"; -import { Button } from "@/components/ui/button"; +import { Button } from "@/components/common/button"; import { LogIn } from "lucide-react"; import { useState } from "react"; @@ -88,4 +88,4 @@ export function HomeNavbar() { )} ); -} \ No newline at end of file +} diff --git a/components/layout/sidebar.tsx b/components/layout/sidebar.tsx index f45ec00..408f6dc 100644 --- a/components/layout/sidebar.tsx +++ b/components/layout/sidebar.tsx @@ -5,12 +5,12 @@ import Link from "next/link" import { useRouter, usePathname } from "next/navigation" import { ShoppingCart, LogOut, Shield } from "lucide-react" import { NavItem } from "./nav-item" -import { Button } from "@/components/ui/button" +import { Button } from "@/components/common/button" import { sidebarConfig } from "@/config/sidebar" import { adminSidebarConfig } from "@/config/admin-sidebar" import { logoutUser } from "@/lib/utils/auth" import { toast } from "sonner" -import { useUser } from "@/hooks/useUser" +import { useUser } from "@/lib/hooks/useUser" const Sidebar: React.FC = () => { const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false) @@ -119,3 +119,5 @@ const Sidebar: React.FC = () => { export default Sidebar + + diff --git a/components/theme-switcher.tsx b/components/layout/theme-switcher.tsx similarity index 95% rename from components/theme-switcher.tsx rename to components/layout/theme-switcher.tsx index 9500b86..3272b69 100644 --- a/components/theme-switcher.tsx +++ b/components/layout/theme-switcher.tsx @@ -2,7 +2,7 @@ import { useTheme } from "next-themes"; import { useEffect, useState } from "react"; -import { Button } from "@/components/ui/button"; +import { Button } from "@/components/common/button"; export function ThemeSwitcher() { const { theme, setTheme } = useTheme(); @@ -27,4 +27,4 @@ export function ThemeSwitcher() { )} ); -} \ No newline at end of file +} diff --git a/components/theme-toggle.tsx b/components/layout/theme-toggle.tsx similarity index 100% rename from components/theme-toggle.tsx rename to components/layout/theme-toggle.tsx diff --git a/components/modals/broadcast-dialog.tsx b/components/modals/broadcast-dialog.tsx index b1a9e7e..3698741 100644 --- a/components/modals/broadcast-dialog.tsx +++ b/components/modals/broadcast-dialog.tsx @@ -1,12 +1,12 @@ "use client"; import { useState, useRef, lazy, Suspense } from "react"; -import { Button } from "@/components/ui/button"; -import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; +import { Button } from "@/components/common/button"; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/common/dialog"; import { Send, Bold, Italic, Code, Link as LinkIcon, Image as ImageIcon, X, Eye, EyeOff, Package } from "lucide-react"; import { toast } from "sonner"; import { clientFetch } from "@/lib/api"; -import { Textarea } from "@/components/ui/textarea"; +import { Textarea } from "@/components/common/textarea"; import Image from "next/image"; import ProductSelector from "./product-selector"; @@ -108,7 +108,7 @@ export default function BroadcastDialog({ open, setOpen }: BroadcastDialogProps) ?.split("=")[1]; if (!authToken) { - document.location.href = "/login"; + document.location.href = "/auth/login"; throw new Error("No authentication token found"); } @@ -365,4 +365,5 @@ __italic text__ ); -} \ No newline at end of file +} + diff --git a/components/modals/image-viewer-modal.tsx b/components/modals/image-viewer-modal.tsx index 269a56d..8e59c93 100644 --- a/components/modals/image-viewer-modal.tsx +++ b/components/modals/image-viewer-modal.tsx @@ -1,6 +1,6 @@ -import { Dialog, DialogContent } from "@/components/ui/dialog"; +import { Dialog, DialogContent } from "@/components/common/dialog"; import { ChevronLeft, ChevronRight, X } from "lucide-react"; -import { Button } from "@/components/ui/button"; +import { Button } from "@/components/common/button"; import { useEffect } from "react"; interface ImageViewerModalProps { @@ -96,4 +96,4 @@ export function ImageViewerModal({ isOpen, onClose, imageUrl, onNavigate }: Imag ); -} \ No newline at end of file +} diff --git a/components/modals/import-products-modal.tsx b/components/modals/import-products-modal.tsx index d45408e..ceb997c 100644 --- a/components/modals/import-products-modal.tsx +++ b/components/modals/import-products-modal.tsx @@ -1,11 +1,11 @@ "use client"; import { useState } from "react"; -import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; -import { Button } from "@/components/ui/button"; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/common/dialog"; +import { Button } from "@/components/common/button"; import { Upload, AlertCircle } from "lucide-react"; import { toast } from "sonner"; -import { Alert, AlertDescription } from "@/components/ui/alert"; +import { Alert, AlertDescription } from "@/components/common/alert"; interface ImportProductsModalProps { open: boolean; @@ -193,4 +193,4 @@ export default function ImportProductsModal({ open, setOpen, onImportComplete }: ); -} \ No newline at end of file +} diff --git a/components/modals/product-modal.tsx b/components/modals/product-modal.tsx index 18b79b8..a8f4098 100644 --- a/components/modals/product-modal.tsx +++ b/components/modals/product-modal.tsx @@ -1,10 +1,10 @@ "use client"; import { useState, useEffect } from "react"; -import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Textarea } from "@/components/ui/textarea"; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/common/dialog"; +import { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; +import { Textarea } from "@/components/common/textarea"; import { Select, SelectContent, @@ -13,7 +13,7 @@ import { SelectLabel, SelectTrigger, SelectValue, -} from "@/components/ui/select"; +} from "@/components/common/select"; import { ImageUpload } from "@/components/forms/image-upload"; import { PricingTiers } from "@/components/forms/pricing-tiers"; import type { ProductModalProps, ProductData } from "@/lib/types"; @@ -21,8 +21,8 @@ import { toast } from "sonner"; import type React from "react"; import { Plus } from "lucide-react"; import { apiRequest } from "@/lib/api"; -import { Switch } from "@/components/ui/switch"; -import { Badge } from "@/components/ui/badge"; +import { Switch } from "@/components/common/switch"; +import { Badge } from "@/components/common/badge"; type CategorySelectProps = { categories: { _id: string; name: string; parentId?: string }[]; @@ -485,4 +485,5 @@ const UnitTypeSelect: React.FC<{
-); \ No newline at end of file +); + diff --git a/components/modals/product-selector.tsx b/components/modals/product-selector.tsx index b32a282..c8e7d75 100644 --- a/components/modals/product-selector.tsx +++ b/components/modals/product-selector.tsx @@ -1,10 +1,10 @@ "use client"; import { useState, useEffect } from "react"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Checkbox } from "@/components/ui/checkbox"; -import { ScrollArea } from "@/components/ui/scroll-area"; +import { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; +import { Checkbox } from "@/components/common/checkbox"; +import { ScrollArea } from "@/components/common/scroll-area"; import { Search, Package } from "lucide-react"; import { apiRequest } from "@/lib/api"; @@ -127,4 +127,5 @@ export default function ProductSelector({ selectedProducts, onSelectionChange }: )} ); -} \ No newline at end of file +} + diff --git a/components/modals/profit-analysis-modal.tsx b/components/modals/profit-analysis-modal.tsx index d82e1fd..d322b1c 100644 --- a/components/modals/profit-analysis-modal.tsx +++ b/components/modals/profit-analysis-modal.tsx @@ -1,10 +1,10 @@ "use client"; import { useState, useEffect } from "react"; -import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; -import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { Badge } from "@/components/ui/badge"; +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/common/dialog"; +import { Button } from "@/components/common/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/common/card"; +import { Badge } from "@/components/common/badge"; import { TrendingUp, TrendingDown, Calculator, DollarSign, Loader2, Info } from "lucide-react"; import { toast } from "sonner"; import { apiRequest } from "@/lib/api"; @@ -336,3 +336,5 @@ export const ProfitAnalysisModal: React.FC = ({ ); }; + + diff --git a/components/modals/shipping-modal.tsx b/components/modals/shipping-modal.tsx index c8af667..220753c 100644 --- a/components/modals/shipping-modal.tsx +++ b/components/modals/shipping-modal.tsx @@ -1,9 +1,9 @@ "use client"; import { ChangeEvent, FormEvent } from "react"; -import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/common/dialog"; +import { Button } from "@/components/common/button"; +import { Input } from "@/components/common/input"; import { ShippingData } from "@/lib/types"; interface ShippingModalProps { @@ -80,3 +80,4 @@ export const ShippingModal = ({ ); }; + diff --git a/components/notifications/OrderNotifications.tsx b/components/notifications/OrderNotifications.tsx index 1fff6e8..d500fa6 100644 --- a/components/notifications/OrderNotifications.tsx +++ b/components/notifications/OrderNotifications.tsx @@ -5,14 +5,14 @@ import { clientFetch } from "@/lib/api"; import { toast } from "sonner"; import { Package, Bell } from "lucide-react"; import { useRouter } from "next/navigation"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/common/badge"; +import { Button } from "@/components/common/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; +} from "@/components/common/dropdown-menu"; interface Order { _id: string; @@ -234,4 +234,5 @@ export default function OrderNotifications() { ); -} \ No newline at end of file +} + diff --git a/components/notifications/UnifiedNotifications.tsx b/components/notifications/UnifiedNotifications.tsx index c19eb6d..66c28f1 100644 --- a/components/notifications/UnifiedNotifications.tsx +++ b/components/notifications/UnifiedNotifications.tsx @@ -2,8 +2,8 @@ import React, { useState } from "react"; import { useRouter } from "next/navigation"; -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/common/badge"; +import { Button } from "@/components/common/button"; import { BellRing, Package, MessageCircle } from "lucide-react"; import { DropdownMenu, @@ -11,8 +11,8 @@ import { DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +} from "@/components/common/dropdown-menu"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/common/tabs"; import { useNotifications } from "@/lib/notification-context"; export default function UnifiedNotifications() { @@ -283,4 +283,4 @@ export default function UnifiedNotifications() { ); -} \ No newline at end of file +} diff --git a/components/tables/order-table.tsx b/components/tables/order-table.tsx index c5bde0f..baf7a42 100644 --- a/components/tables/order-table.tsx +++ b/components/tables/order-table.tsx @@ -11,16 +11,16 @@ import { TableHead, TableHeader, TableRow, -} from "@/components/ui/table"; -import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; +} from "@/components/common/table"; +import { Button } from "@/components/common/button"; +import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/common/card"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from "@/components/ui/select"; +} from "@/components/common/select"; import { Eye, Loader2, @@ -37,10 +37,10 @@ import { } from "lucide-react"; import Link from "next/link"; import { clientFetch } from '@/lib/api'; -import { exportOrdersToCSV } from '@/lib/api-client'; +import { exportOrdersToCSV } from '@/lib/api/api-client'; import { toast } from "sonner"; import { Download } from "lucide-react"; -import { Checkbox } from "@/components/ui/checkbox"; +import { Checkbox } from "@/components/common/checkbox"; import { AlertDialog, AlertDialogAction, @@ -51,8 +51,8 @@ import { AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, -} from "@/components/ui/alert-dialog"; -import { cacheUtils } from '@/lib/api-client'; +} from "@/components/common/alert-dialog"; +import { cacheUtils } from '@/lib/api/api-client'; interface Order { _id: string; @@ -815,4 +815,5 @@ export default function OrderTable() { ); -} \ No newline at end of file +} + diff --git a/components/tables/product-table.tsx b/components/tables/product-table.tsx index 442e665..adcaebb 100644 --- a/components/tables/product-table.tsx +++ b/components/tables/product-table.tsx @@ -5,7 +5,7 @@ import { TableHead, TableHeader, TableRow, -} from "@/components/ui/table"; +} from "@/components/common/table"; import Image from "next/image"; import { Edit, @@ -18,12 +18,12 @@ import { PackageX, Archive } from "lucide-react"; -import { Button } from "@/components/ui/button"; +import { Button } from "@/components/common/button"; import React, { useState, useEffect } from "react"; -import { Product } from "@/models/products"; -import { Badge } from "@/components/ui/badge"; -import { Switch } from "@/components/ui/switch"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Product } from "@/lib/models/products"; +import { Badge } from "@/components/common/badge"; +import { Switch } from "@/components/common/switch"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/common/card"; import { motion, AnimatePresence } from "framer-motion"; const getProductImageUrl = (product: Product) => { @@ -329,3 +329,5 @@ const ProductTable = ({ }; export default ProductTable; + + diff --git a/components/tables/shipping-table.tsx b/components/tables/shipping-table.tsx index 6f40071..19510b0 100644 --- a/components/tables/shipping-table.tsx +++ b/components/tables/shipping-table.tsx @@ -1,5 +1,5 @@ import React, { useState, useEffect } from "react"; -import { Skeleton } from "@/components/ui/skeleton"; +import { Skeleton } from "@/components/common/skeleton"; import { Table, TableBody, @@ -7,10 +7,10 @@ import { TableHead, TableHeader, TableRow, -} from "@/components/ui/table"; -import { Button } from "@/components/ui/button"; +} from "@/components/common/table"; +import { Button } from "@/components/common/button"; import { Edit, Trash, Truck, PackageX } from "lucide-react"; -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/common/card"; import { motion, AnimatePresence } from "framer-motion"; import { ShippingMethod } from "@/lib/types"; @@ -185,3 +185,4 @@ export const ShippingTable: React.FC = ({ ); }; + diff --git a/components/ui/accordion.tsx b/components/ui/accordion.tsx deleted file mode 100644 index 070c70f..0000000 --- a/components/ui/accordion.tsx +++ /dev/null @@ -1,58 +0,0 @@ -"use client" - -import * as React from "react" -import * as AccordionPrimitive from "@radix-ui/react-accordion" -import { ChevronDown } from "lucide-react" - -import { cn } from "@/lib/utils/styles"; - -const Accordion = AccordionPrimitive.Root - -const AccordionItem = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)) -AccordionItem.displayName = "AccordionItem" - -const AccordionTrigger = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - - svg]:rotate-180", - className - )} - {...props} - > - {children} - - - -)) -AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName - -const AccordionContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( - -
{children}
-
-)) - -AccordionContent.displayName = AccordionPrimitive.Content.displayName - -export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } diff --git a/lib/api-client.ts b/lib/api/api-client.ts similarity index 99% rename from lib/api-client.ts rename to lib/api/api-client.ts index c7e7781..a8e2b69 100644 --- a/lib/api-client.ts +++ b/lib/api/api-client.ts @@ -4,7 +4,7 @@ let toast: any; try { // Try to import from the UI components - toast = require("@/components/ui/use-toast").toast; + toast = require("@/components/common/use-toast").toast; } catch (error) { // Fallback toast function if not available toast = { @@ -512,4 +512,4 @@ export async function clientFetchWithCache( } return result; -} \ No newline at end of file +} diff --git a/lib/api.ts b/lib/api/index.ts similarity index 91% rename from lib/api.ts rename to lib/api/index.ts index 5ab537e..accdbab 100644 --- a/lib/api.ts +++ b/lib/api/index.ts @@ -33,7 +33,7 @@ export { type Product, type ProductsResponse, type StockData, -} from './services/product-service'; +} from '../services/product-service'; // Re-export shipping services export { @@ -46,7 +46,7 @@ export { // Types type ShippingOption, type ShippingOptionsResponse, -} from './services/shipping-service'; +} from '../services/shipping-service'; // Re-export analytics services export { @@ -61,15 +61,15 @@ export { getCustomerInsightsWithStore, getOrderAnalyticsWithStore, getStoreIdForUser, - formatGBP, - // Types type AnalyticsOverview, type RevenueData, type ProductPerformance, type CustomerInsights, type OrderAnalytics, -} from './services/analytics-service'; +} from '../services/analytics-service'; + +export { formatGBP, formatNumber } from '../utils/format'; // Define the PlatformStats interface to match the expected format export interface PlatformStats { @@ -89,7 +89,7 @@ export interface PlatformStats { export { getPlatformStats, getVendorStats, -} from './services/stats-service'; +} from '../services/stats-service'; // Re-export server API functions export { @@ -111,8 +111,8 @@ export { // Get clientFetch first so we can use it in the compatibility functions import { clientFetch } from './api-client'; -import { getProductDetails, updateProduct } from './services/product-service'; -import { getPlatformStats } from './services/stats-service'; +import { getProductDetails, updateProduct } from '../services/product-service'; +import { getPlatformStats } from '../services/stats-service'; // Add missing functions for backward compatibility // These are functions from the old style that we need to maintain compatibility diff --git a/lib/server-api.ts b/lib/api/server-api.ts similarity index 97% rename from lib/server-api.ts rename to lib/api/server-api.ts index fcbd0d0..f466d5b 100644 --- a/lib/server-api.ts +++ b/lib/api/server-api.ts @@ -22,20 +22,20 @@ try { */ function getServerApiUrl(endpoint: string): string { const apiUrl = process.env.SERVER_API_URL || 'http://localhost:3001/api'; - + // Validate API URL to prevent 500 errors if (!apiUrl || apiUrl === 'undefined' || apiUrl === 'null') { console.warn('SERVER_API_URL not properly set, using localhost fallback'); const fallbackUrl = 'http://localhost:3001/api'; const cleanEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint; - return fallbackUrl.endsWith('/') + return fallbackUrl.endsWith('/') ? `${fallbackUrl}${cleanEndpoint}` : `${fallbackUrl}/${cleanEndpoint}`; } - + const cleanEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint; - - return apiUrl.endsWith('/') + + return apiUrl.endsWith('/') ? `${apiUrl}${cleanEndpoint}` : `${apiUrl}/${cleanEndpoint}`; } @@ -58,18 +58,18 @@ export async function fetchServer( "For client components, use clientFetch or fetchClient instead." ); } - + // Get auth token from cookies const cookieStore = await cookiesModule.cookies(); const authToken = cookieStore.get('Authorization')?.value; // Redirect to login if not authenticated - if (!authToken) redirect('/login'); - + if (!authToken) redirect('/auth/login'); + try { // Get the complete backend URL using the utility function const url = getServerApiUrl(endpoint); - + // Make the request with proper auth headers const res = await fetch(url, { ...options, @@ -82,8 +82,8 @@ export async function fetchServer( }); // Handle auth failures - if (res.status === 401) redirect('/login'); - + if (res.status === 401) redirect('/auth/login'); + // Handle other errors if (!res.ok) { let errorData; @@ -92,7 +92,7 @@ export async function fetchServer( } catch { errorData = {}; } - + // Handle new error format: { success: false, error: { message: "...", code: "..." } } // or old format: { error: "...", message: "..." } let errorMessage: string; @@ -105,7 +105,7 @@ export async function fetchServer( } else { errorMessage = `Request failed: ${res.status} ${res.statusText}`; } - + throw new Error(errorMessage); } @@ -158,22 +158,22 @@ export const getCustomerDetailsServer = async (userId: string): Promise 0) { return data; } - + console.info('Using sample stats data for demo'); return { orders: { @@ -296,7 +296,7 @@ export const getAnalyticsOverviewServer = async (storeId?: string): Promise => { const params = new URLSearchParams({ period }); if (storeId) params.append('storeId', storeId); - + const url = `/analytics/revenue-trends?${params.toString()}`; return fetchServer(url); }; @@ -314,7 +314,7 @@ export const getCustomerInsightsServer = async (storeId?: string): Promise => { const params = new URLSearchParams({ period }); if (storeId) params.append('storeId', storeId); - + const url = `/analytics/order-analytics?${params.toString()}`; return fetchServer(url); }; \ No newline at end of file diff --git a/hooks/use-chromebook-keyboard.tsx b/lib/hooks/use-chromebook-keyboard.tsx similarity index 100% rename from hooks/use-chromebook-keyboard.tsx rename to lib/hooks/use-chromebook-keyboard.tsx diff --git a/hooks/use-chromebook-scroll.tsx b/lib/hooks/use-chromebook-scroll.tsx similarity index 100% rename from hooks/use-chromebook-scroll.tsx rename to lib/hooks/use-chromebook-scroll.tsx diff --git a/hooks/use-mobile.tsx b/lib/hooks/use-mobile.tsx similarity index 100% rename from hooks/use-mobile.tsx rename to lib/hooks/use-mobile.tsx diff --git a/components/ui/use-toast.ts b/lib/hooks/use-toast.ts similarity index 99% rename from components/ui/use-toast.ts rename to lib/hooks/use-toast.ts index 02e111d..714ad09 100644 --- a/components/ui/use-toast.ts +++ b/lib/hooks/use-toast.ts @@ -6,7 +6,7 @@ import * as React from "react" import type { ToastActionElement, ToastProps, -} from "@/components/ui/toast" +} from "@/components/common/toast" const TOAST_LIMIT = 1 const TOAST_REMOVE_DELAY = 1000000 diff --git a/hooks/useFilterState.ts b/lib/hooks/useFilterState.ts similarity index 100% rename from hooks/useFilterState.ts rename to lib/hooks/useFilterState.ts diff --git a/hooks/useKeepOnline.ts b/lib/hooks/useKeepOnline.ts similarity index 100% rename from hooks/useKeepOnline.ts rename to lib/hooks/useKeepOnline.ts diff --git a/hooks/useUser.ts b/lib/hooks/useUser.ts similarity index 94% rename from hooks/useUser.ts rename to lib/hooks/useUser.ts index d8ca321..9aa2785 100644 --- a/hooks/useUser.ts +++ b/lib/hooks/useUser.ts @@ -1,7 +1,7 @@ "use client" import { useState, useEffect } from 'react' -import { clientFetch } from '@/lib/api-client' +import { clientFetch } from '@/lib/api/api-client' interface Vendor { _id: string; diff --git a/hooks/useWidgetLayout.ts b/lib/hooks/useWidgetLayout.ts similarity index 79% rename from hooks/useWidgetLayout.ts rename to lib/hooks/useWidgetLayout.ts index 51be753..bffebad 100644 --- a/hooks/useWidgetLayout.ts +++ b/lib/hooks/useWidgetLayout.ts @@ -1,57 +1,7 @@ "use client" import { useState, useEffect, useCallback } from "react" - -// Per-widget settings types -export interface RecentActivitySettings { - itemCount: number // 5, 10, 15 -} - -export interface TopProductsSettings { - itemCount: number // 3, 5, 10 - showRevenue: boolean -} - -export interface OverviewSettings { - showChange: boolean // Show % change from previous period -} - -export interface RevenueChartSettings { - days: number // 7, 14, 30 - showComparison: boolean -} - -export interface LowStockSettings { - threshold: number // Show items with stock below this - itemCount: number -} - -export interface RecentCustomersSettings { - itemCount: number - showSpent: boolean -} - -export interface PendingChatsSettings { - showPreview: boolean -} - -export type WidgetSettings = - | { type: "quick-actions" } - | { type: "overview"; settings: OverviewSettings } - | { type: "recent-activity"; settings: RecentActivitySettings } - | { type: "top-products"; settings: TopProductsSettings } - | { type: "revenue-chart"; settings: RevenueChartSettings } - | { type: "low-stock"; settings: LowStockSettings } - | { type: "recent-customers"; settings: RecentCustomersSettings } - | { type: "pending-chats"; settings: PendingChatsSettings } - -export interface WidgetConfig { - id: string - title: string - visible: boolean - order: number - settings?: Record -} +import type { WidgetConfig, WidgetSettings } from "@/lib/types/dashboard" const DEFAULT_WIDGETS: WidgetConfig[] = [ { id: "quick-actions", title: "Quick Actions", visible: true, order: 0 }, diff --git a/models/categories.ts b/lib/models/categories.ts similarity index 100% rename from models/categories.ts rename to lib/models/categories.ts diff --git a/models/products.ts b/lib/models/products.ts similarity index 100% rename from models/products.ts rename to lib/models/products.ts diff --git a/lib/notification-context.tsx b/lib/notification-context.tsx index 0a500fe..3ab209e 100644 --- a/lib/notification-context.tsx +++ b/lib/notification-context.tsx @@ -4,7 +4,7 @@ import React, { createContext, useContext, useState, useEffect, useRef, ReactNod import { clientFetch } from "@/lib/api"; import { toast } from "sonner"; import { getCookie } from "@/lib/api"; -import { cacheUtils } from '@/lib/api-client'; +import { cacheUtils } from '@/lib/api/api-client'; import { Package } from "lucide-react"; interface Order { @@ -343,4 +343,4 @@ export function useNotifications() { throw new Error('useNotifications must be used within a NotificationProvider'); } return context; -} \ No newline at end of file +} diff --git a/lib/services/analytics-service.ts b/lib/services/analytics-service.ts index d59c507..f74d926 100644 --- a/lib/services/analytics-service.ts +++ b/lib/services/analytics-service.ts @@ -1,6 +1,7 @@ "use client"; -import { clientFetch } from "../api-client"; +import { clientFetch } from '@/lib/api/api-client'; +import { formatGBP } from '@/lib/utils/format'; // Analytics Types export interface AnalyticsOverview { @@ -293,15 +294,6 @@ export const getGrowthAnalyticsWithStore = return getGrowthAnalytics(storeId); }; -export function formatGBP(value: number) { - return value.toLocaleString("en-GB", { - style: "currency", - currency: "GBP", - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }); -} - // Prediction Types export interface SalesPrediction { predicted: number | null; diff --git a/services/customerService.ts b/lib/services/customerService.ts similarity index 100% rename from services/customerService.ts rename to lib/services/customerService.ts diff --git a/lib/services/index.ts b/lib/services/index.ts index da3dbb0..b4117f5 100644 --- a/lib/services/index.ts +++ b/lib/services/index.ts @@ -1,5 +1,3 @@ -// Re-export all service functionality -export * from './product-service'; -export * from './shipping-service'; -export * from './stats-service'; -export * from '../api-client'; \ No newline at end of file +// Re-export all services from the lib directory +// This provides backward compatibility +export * from '../lib/api'; \ No newline at end of file diff --git a/lib/services/product-service.ts b/lib/services/product-service.ts index 21db135..5cfe057 100644 --- a/lib/services/product-service.ts +++ b/lib/services/product-service.ts @@ -1,4 +1,4 @@ -import { clientFetch } from '../api-client'; +import { clientFetch } from '@/lib/api/api-client'; // Product data types export interface Product { diff --git a/lib/services/shipping-service.ts b/lib/services/shipping-service.ts index 092c3e6..6d59975 100644 --- a/lib/services/shipping-service.ts +++ b/lib/services/shipping-service.ts @@ -1,4 +1,4 @@ -import { clientFetch } from '../api-client'; +import { clientFetch } from '@/lib/api/api-client'; /** * Shipping service - Handles shipping options diff --git a/lib/services/stats-service.ts b/lib/services/stats-service.ts index 110cafd..b594630 100644 --- a/lib/services/stats-service.ts +++ b/lib/services/stats-service.ts @@ -1,4 +1,4 @@ -import { clientFetch } from '../api-client'; +import { clientFetch } from '@/lib/api/api-client'; // Stats data types export interface PlatformStats { diff --git a/lib/types/dashboard.ts b/lib/types/dashboard.ts new file mode 100644 index 0000000..c9e0e09 --- /dev/null +++ b/lib/types/dashboard.ts @@ -0,0 +1,61 @@ +export interface RecentActivitySettings { + itemCount: number // 5, 10, 15 +} + +export interface TopProductsSettings { + itemCount: number // 3, 5, 10 + showRevenue: boolean +} + +export interface OverviewSettings { + showChange: boolean // Show % change from previous period +} + +export interface RevenueChartSettings { + days: number // 7, 14, 30 + showComparison: boolean +} + +export interface LowStockSettings { + threshold: number // Show items with stock below this + itemCount: number +} + +export interface RecentCustomersSettings { + itemCount: number + showSpent: boolean +} + +export interface PendingChatsSettings { + showPreview: boolean +} + +export type WidgetSettings = + | { type: "quick-actions" } + | { type: "overview"; settings: OverviewSettings } + | { type: "recent-activity"; settings: RecentActivitySettings } + | { type: "top-products"; settings: TopProductsSettings } + | { type: "revenue-chart"; settings: RevenueChartSettings } + | { type: "low-stock"; settings: LowStockSettings } + | { type: "recent-customers"; settings: RecentCustomersSettings } + | { type: "pending-chats"; settings: PendingChatsSettings } + +export interface WidgetConfig { + id: string + title: string + visible: boolean + order: number + colSpan?: number + settings?: Record +} + +export interface TopProduct { + id: string; + name: string; + price: number | number[]; + image: string; + count: number; + revenue: number; + unitType?: string; + currentStock?: number; +} diff --git a/lib/utils/format.ts b/lib/utils/format.ts new file mode 100644 index 0000000..3ebb021 --- /dev/null +++ b/lib/utils/format.ts @@ -0,0 +1,28 @@ +export const formatCurrency = (amount: number | undefined | null): string => { + if (amount === undefined || amount === null || isNaN(Number(amount))) { + return '£0.00'; + } + return new Intl.NumberFormat('en-GB', { + style: 'currency', + currency: 'GBP' + }).format(Number(amount)); +}; + +export function formatGBP(value: number | undefined | null) { + if (value === undefined || value === null || isNaN(Number(value))) { + return '£0.00'; + } + return Number(value).toLocaleString('en-GB', { + style: 'currency', + currency: 'GBP', + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }); +} + +export function formatNumber(value: number | undefined | null, options: Intl.NumberFormatOptions = {}) { + if (value === undefined || value === null || isNaN(Number(value))) { + return '0'; + } + return Number(value).toLocaleString('en-GB', options); +} \ No newline at end of file diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 0000000..77e3cff --- /dev/null +++ b/middleware.ts @@ -0,0 +1,25 @@ +import { NextResponse } from 'next/server'; +import type { NextRequest } from 'next/server'; + +export function middleware(request: NextRequest) { + const { pathname } = request.nextUrl; + + // Protect dashboard routes + if (pathname.startsWith('/dashboard')) { + const authToken = request.cookies.get('Authorization')?.value; + + if (!authToken) { + // Redirect to login if no token is found + const loginUrl = new URL('/auth/login', request.url); + loginUrl.searchParams.set('redirectUrl', pathname); + return NextResponse.redirect(loginUrl); + } + } + + return NextResponse.next(); +} + +// See "Matching Paths" below to learn more +export const config = { + matcher: ['/dashboard/:path*'], +}; diff --git a/public/git-info.json b/public/git-info.json index be281d7..c6ce719 100644 --- a/public/git-info.json +++ b/public/git-info.json @@ -1,4 +1,4 @@ { - "commitHash": "a6b7286", - "buildTime": "2026-01-12T10:20:09.966Z" + "commitHash": "a6e6cd0", + "buildTime": "2026-01-13T04:57:00.870Z" } \ No newline at end of file diff --git a/services/index.ts b/services/index.ts deleted file mode 100644 index b4117f5..0000000 --- a/services/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Re-export all services from the lib directory -// This provides backward compatibility -export * from '../lib/api'; \ No newline at end of file diff --git a/tailwind.config.ts b/tailwind.config.ts index 439b464..f7d21f2 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -8,8 +8,6 @@ const config: Config = { "./app/**/*.{js,ts,jsx,tsx,mdx}", "./src/**/*.{ts,tsx}", "./lib/**/*.{js,ts,jsx,tsx,mdx}", - "./hooks/**/*.{js,ts,jsx,tsx,mdx}", - "./utils/**/*.{js,ts,jsx,tsx,mdx}", ], theme: { container: { @@ -46,11 +44,11 @@ const config: Config = { '50%': { opacity: '1', transform: 'scale(1) rotate(180deg)' } }, glow: { - '0%, 100%': { - boxShadow: '0 0 5px hsl(0 84% 50%), 0 0 10px hsl(0 84% 50%), 0 0 15px hsl(0 84% 50%)' + '0%, 100%': { + boxShadow: '0 0 5px hsl(0 84% 50%), 0 0 10px hsl(0 84% 50%), 0 0 15px hsl(0 84% 50%)' }, - '50%': { - boxShadow: '0 0 10px hsl(142 76% 36%), 0 0 20px hsl(142 76% 36%), 0 0 30px hsl(142 76% 36%)' + '50%': { + boxShadow: '0 0 10px hsl(142 76% 36%), 0 0 20px hsl(142 76% 36%), 0 0 30px hsl(142 76% 36%)' } } }, diff --git a/utils/format.ts b/utils/format.ts deleted file mode 100644 index 62d055f..0000000 --- a/utils/format.ts +++ /dev/null @@ -1,15 +0,0 @@ -export const formatCurrency = (amount: number): string => { - return new Intl.NumberFormat('en-GB', { - style: 'currency', - currency: 'GBP' - }).format(amount); -}; - -export function formatGBP(value: number) { - return value.toLocaleString('en-GB', { - style: 'currency', - currency: 'GBP', - minimumFractionDigits: 2, - maximumFractionDigits: 2, - }); -} \ No newline at end of file