diff --git a/app/login/page.tsx b/app/auth/login/page.tsx similarity index 95% rename from app/login/page.tsx rename to app/auth/login/page.tsx index 4c41563..f4bdf93 100644 --- a/app/login/page.tsx +++ b/app/auth/login/page.tsx @@ -1,3 +1,4 @@ +import dataService from '@/lib/data-service'; "use client"; import { useState } from "react"; @@ -20,7 +21,7 @@ export default function LoginPage() { e.preventDefault(); setError(""); - const res = await fetch("https://internal-api.inboxi.ng/api/auth/login", { + const res = await dataService.fetchData("https://internal-api.inboxi.ng/api/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username, password }), diff --git a/app/register/page.tsx b/app/auth/register/page.tsx similarity index 96% rename from app/register/page.tsx rename to app/auth/register/page.tsx index 7a5714d..041b449 100644 --- a/app/register/page.tsx +++ b/app/auth/register/page.tsx @@ -1,3 +1,4 @@ +import dataService from '@/lib/data-service'; "use client"; import { useState } from "react"; @@ -21,7 +22,7 @@ export default function RegisterPage() { setError(""); setLoading(true); - const res = await fetch("https://internal-api.inboxi.ng/api/auth/register", { + const res = await dataService.fetchData("https://internal-api.inboxi.ng/api/auth/register", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ username, password, invitationCode }), diff --git a/app/dashboard/orders/[id]/page.tsx b/app/dashboard/orders/[id]/page.tsx index 8d968e7..6c43bad 100644 --- a/app/dashboard/orders/[id]/page.tsx +++ b/app/dashboard/orders/[id]/page.tsx @@ -1,8 +1,10 @@ + "use client"; +import { fetchData } from '@/lib/data-service'; import { useEffect, useState } from "react"; import { useParams } from "next/navigation"; -import Layout from "@/components/kokonutui/layout"; +import Layout from "@/components/layout/layout"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; @@ -55,10 +57,34 @@ export default function OrderDetailsPage() { const params = useParams(); const orderId = params?.id; + const fetchProductNames = async ( + productIds: string[], + authToken: string + ): Promise> => { + const productNamesMap: Record = {}; + try { + const promises = productIds.map((id) => + fetchData(`${process.env.NEXT_PUBLIC_API_URL}/products/${id}`, { + method: "GET", + headers: { Authorization: `Bearer ${authToken}` }, + }) + ); + const responses = await Promise.all(promises); + const results = await Promise.all(responses.map((res) => res)); + + results.forEach((product, index) => { + productNamesMap[productIds[index]] = product.name || "Unknown Product"; + }); + } catch (err) { + console.error("Failed to fetch product names:", err); + } + return productNamesMap; + }; + const handleMarkAsPaid = async () => { try { const authToken = document.cookie.split("Authorization=")[1]; - const response = await fetch( + const response = await fetchData( `${process.env.NEXT_PUBLIC_API_URL}/orders/${orderId}`, { method: "PUT", @@ -70,7 +96,7 @@ export default function OrderDetailsPage() { } ); - if (response.ok) { + if (response && response.message === "Order status updated successfully") { setIsPaid(true); // Update isPaid state setOrder((prevOrder) => (prevOrder ? { ...prevOrder, status: "paid" } : null)); // Update order status console.log("Order marked as paid successfully."); @@ -92,7 +118,7 @@ export default function OrderDetailsPage() { const authToken = document.cookie.split("Authorization=")[1]; - const res = await fetch( + const res = await fetchData( `${process.env.NEXT_PUBLIC_API_URL}/orders/${orderId}`, { method: "GET", @@ -100,9 +126,9 @@ export default function OrderDetailsPage() { } ); - if (!res.ok) throw new Error("Failed to fetch order details"); + if (!res) throw new Error("Failed to fetch order details"); - const data: Order = await res.json(); + const data: Order = await res; setOrder(data); const productIds = data.products.map((product) => product.productId); @@ -120,32 +146,8 @@ export default function OrderDetailsPage() { } }; - const fetchProductNames = async ( - productIds: string[], - authToken: string - ): Promise> => { - const productNamesMap: Record = {}; - try { - const promises = productIds.map((id) => - fetch(`${process.env.NEXT_PUBLIC_API_URL}/products/${id}`, { - method: "GET", - headers: { Authorization: `Bearer ${authToken}` }, - }) - ); - const responses = await Promise.all(promises); - const results = await Promise.all(responses.map((res) => res.json())); - - results.forEach((product, index) => { - productNamesMap[productIds[index]] = product.name || "Unknown Product"; - }); - } catch (err) { - console.error("Failed to fetch product names:", err); - } - return productNamesMap; - }; - fetchOrderDetails(); - }, [orderId]); +}, [orderId]); const handleAddTracking = async () => { if (!trackingNumber) return; @@ -153,7 +155,7 @@ export default function OrderDetailsPage() { try { const authToken = document.cookie.split("Authorization=")[1]; - const res = await fetch( + const res = await fetchData( `${process.env.NEXT_PUBLIC_API_URL}/orders/${orderId}/tracking`, { method: "PUT", diff --git a/app/dashboard/orders/page.tsx b/app/dashboard/orders/page.tsx index 98148a9..cb2cfd6 100644 --- a/app/dashboard/orders/page.tsx +++ b/app/dashboard/orders/page.tsx @@ -2,9 +2,9 @@ import { useEffect } from "react"; import { useRouter } from "next/navigation"; -import Dashboard from "@/components/kokonutui/dashboard"; +import Dashboard from "@/components/dashboard/dashboard"; import { Package } from "lucide-react"; -import OrderTable from "@/components/order-table"; +import OrderTable from "@/components/tables/order-table"; export default function OrdersPage() { const router = useRouter(); diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx index 780292f..840fc25 100644 --- a/app/dashboard/page.tsx +++ b/app/dashboard/page.tsx @@ -1,6 +1,6 @@ -import Dashboard from "@/components/kokonutui/dashboard"; -import Content from "@/components/kokonutui/content"; -import { fetchServer } from "@/lib/server-utils" +import Dashboard from "@/components/dashboard/dashboard"; +import Content from "@/components/dashboard/content"; +import { fetchServer } from '@/lib/server-service'; // ✅ Corrected Vendor Type interface Vendor { diff --git a/app/dashboard/products/page.tsx b/app/dashboard/products/page.tsx index 8df8d75..f777fd3 100644 --- a/app/dashboard/products/page.tsx +++ b/app/dashboard/products/page.tsx @@ -2,7 +2,7 @@ import { useState, useEffect, ChangeEvent } from "react"; import { useRouter } from "next/navigation"; -import Layout from "@/components/kokonutui/layout"; +import Layout from "@/components/layout/layout"; import { Button } from "@/components/ui/button"; import { Product } from "@/models/products"; import { Plus } from "lucide-react"; @@ -11,8 +11,8 @@ import { saveProductData, deleteProductData, } from "@/lib/productData"; -import { ProductModal } from "@/components/product-modal"; -import ProductTable from "@/components/product-table"; +import { ProductModal } from "@/components/modals/product-modal"; +import ProductTable from "@/components/tables/product-table"; export default function ProductsPage() { const router = useRouter(); @@ -34,14 +34,14 @@ export default function ProductsPage() { // Fetch products and categories useEffect(() => { const authToken = document.cookie - .split("; ") - .find((row) => row.startsWith("Authorization=")) - ?.split("=")[1]; + .split("; ") + .find((row) => row.startsWith("Authorization=")) + ?.split("=")[1]; - if (!authToken) { - router.push("/login"); - return; - } + if (!authToken) { + router.push("/login"); + return; + } const fetchDataAsync = async () => { try { @@ -55,8 +55,16 @@ export default function ProductsPage() { authToken ), ]); - - setProducts(fetchedProducts); + + console.log("Fetched Products:", fetchedProducts); + + // Ensure all products have tieredPricing + const processedProducts = fetchedProducts.map((product: Product) => ({ + ...product, + tieredPricing: product.tieredPricing || [{ minQuantity: 1, pricePerUnit: 0 }], + })); + + setProducts(processedProducts); setCategories(fetchedCategories); } catch (error) { console.error("Error loading data:", error); @@ -150,10 +158,12 @@ export default function ProductsPage() { const handleEditProduct = (product: Product) => { setProductData({ ...product, - tieredPricing: product.tieredPricing.map(tier => ({ - minQuantity: tier.minQuantity, - pricePerUnit: tier.pricePerUnit - })), + tieredPricing: product.tieredPricing + ? product.tieredPricing.map(tier => ({ + minQuantity: tier.minQuantity, + pricePerUnit: tier.pricePerUnit + })) + : [{ minQuantity: 1, pricePerUnit: 0 }], // Fallback if undefined }); setEditing(true); setModalOpen(true); diff --git a/app/dashboard/shipping/page.tsx b/app/dashboard/shipping/page.tsx index 5c6b7a8..e5108b0 100644 --- a/app/dashboard/shipping/page.tsx +++ b/app/dashboard/shipping/page.tsx @@ -2,10 +2,10 @@ import { useState, useEffect, ChangeEvent } from "react"; import { useRouter } from "next/navigation"; -import Layout from "@/components/kokonutui/layout"; +import Layout from "@/components/layout/layout"; import { Edit, Plus, Trash } from "lucide-react"; import { Button } from "@/components/ui/button"; -import { ShippingModal } from "@/components/shipping-modal"; +import { ShippingModal } from "@/components/modals/shipping-modal"; import { Skeleton } from "@/components/ui/skeleton"; import { fetchShippingMethods, @@ -16,7 +16,7 @@ import { import { ShippingMethod, ShippingData } from "@/lib/types"; -import { ShippingTable } from "@/components/shipping-table"; +import { ShippingTable } from "@/components/tables/shipping-table"; export default function ShippingPage() { const [shippingMethods, setShippingMethods] = useState([]); @@ -54,6 +54,8 @@ export default function ShippingPage() { }) ); + console.log("Fetched Shipping Methods:", sanitizedMethods); + setShippingMethods(sanitizedMethods); } catch (error) { console.error("Error loading shipping options:", error); diff --git a/app/dashboard/storefront/page.tsx b/app/dashboard/storefront/page.tsx index 446ec23..af1fc76 100644 --- a/app/dashboard/storefront/page.tsx +++ b/app/dashboard/storefront/page.tsx @@ -2,14 +2,14 @@ import { useState, useEffect, ChangeEvent } from "react"; import { useRouter } from "next/navigation"; -import Layout from "@/components/kokonutui/layout"; +import Layout from "@/components/layout/layout"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { Save, Send, Key, MessageSquare, Shield } from "lucide-react"; import { apiRequest } from "@/lib/storeHelper"; import { toast, Toaster } from "sonner"; -import BroadcastDialog from "@/components/broadcast-dialog"; +import BroadcastDialog from "@/components/modals/broadcast-dialog"; // ✅ Define the Storefront Type interface Storefront { diff --git a/app/layout.tsx b/app/layout.tsx index 8a1a172..df56de4 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,6 +1,6 @@ import { Inter } from "next/font/google" import "./globals.css" -import { ThemeProvider } from "@/components/theme-provider" +import { ThemeProvider } from "@/components/layout/theme-provider"; const inter = Inter({ subsets: ["latin"] }) diff --git a/components.json b/components/components.json similarity index 100% rename from components.json rename to components/components.json diff --git a/components/kokonutui/content.tsx b/components/dashboard/content.tsx similarity index 100% rename from components/kokonutui/content.tsx rename to components/dashboard/content.tsx diff --git a/components/kokonutui/dashboard.tsx b/components/dashboard/dashboard.tsx similarity index 80% rename from components/kokonutui/dashboard.tsx rename to components/dashboard/dashboard.tsx index ab99abd..5bec345 100644 --- a/components/kokonutui/dashboard.tsx +++ b/components/dashboard/dashboard.tsx @@ -1,5 +1,5 @@ import type React from "react" -import Layout from "./layout" +import Layout from "../layout/layout" export default function Dashboard({ children }: { children: React.ReactNode }) { return {children} diff --git a/components/kokonutui/order-stats.tsx b/components/dashboard/order-stats.tsx similarity index 100% rename from components/kokonutui/order-stats.tsx rename to components/dashboard/order-stats.tsx diff --git a/components/kokonutui/layout.tsx b/components/layout/layout.tsx similarity index 100% rename from components/kokonutui/layout.tsx rename to components/layout/layout.tsx diff --git a/components/kokonutui/sidebar.tsx b/components/layout/sidebar.tsx similarity index 96% rename from components/kokonutui/sidebar.tsx rename to components/layout/sidebar.tsx index 46cde85..042856d 100644 --- a/components/kokonutui/sidebar.tsx +++ b/components/layout/sidebar.tsx @@ -1,5 +1,7 @@ "use client"; +import { fetchData } from '@/lib/data-service'; + import { useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; @@ -32,14 +34,14 @@ const NavItem = ({ href, icon: Icon, children, onClick }: NavItemProps) => ( ); -export default function Sidebar() { +function Sidebar() { const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const router = useRouter(); const handleLogout = async () => { try { const authToken = document.cookie.split("authToken=")[1]; - const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/logout`, { + const res = await fetchData(`${process.env.NEXT_PUBLIC_API_URL}/logout`, { method: "POST", headers: { Authorization: `Bearer ${authToken}` }, credentials: "include", @@ -137,4 +139,6 @@ export default function Sidebar() { )} ); -} \ No newline at end of file +} + +export default Sidebar; diff --git a/components/theme-provider.tsx b/components/layout/theme-provider.tsx similarity index 100% rename from components/theme-provider.tsx rename to components/layout/theme-provider.tsx diff --git a/components/broadcast-dialog.tsx b/components/modals/broadcast-dialog.tsx similarity index 100% rename from components/broadcast-dialog.tsx rename to components/modals/broadcast-dialog.tsx diff --git a/components/product-modal.tsx b/components/modals/product-modal.tsx similarity index 100% rename from components/product-modal.tsx rename to components/modals/product-modal.tsx diff --git a/components/shipping-modal.tsx b/components/modals/shipping-modal.tsx similarity index 100% rename from components/shipping-modal.tsx rename to components/modals/shipping-modal.tsx diff --git a/components/order-table.tsx b/components/tables/order-table.tsx similarity index 98% rename from components/order-table.tsx rename to components/tables/order-table.tsx index 04dd1d6..ac2ef73 100644 --- a/components/order-table.tsx +++ b/components/tables/order-table.tsx @@ -28,7 +28,7 @@ import { Truck, } from "lucide-react"; import Link from "next/link"; -import { fetchClient } from "@/lib/client-utils"; +import { clientFetch } from '@/lib/client-utils'; import { toast } from "sonner"; import { Checkbox } from "@/components/ui/checkbox"; @@ -59,7 +59,7 @@ export default function OrderTable() { const fetchOrders = useCallback(async () => { try { setLoading(true); - const data = await fetchClient<{ orders: Order[] }>("/orders"); + const data = await clientFetch("/orders"); setOrders(data.orders || []); } catch (error) { toast.error("Failed to fetch orders"); @@ -132,7 +132,7 @@ export default function OrderTable() { } try { - await fetchClient("/orders/mark-shipped", { + await clientFetch("/orders/mark-shipped", { method: "POST", body: JSON.stringify({ orderIds: Array.from(selectedOrders) }) }); diff --git a/components/product-table.tsx b/components/tables/product-table.tsx similarity index 100% rename from components/product-table.tsx rename to components/tables/product-table.tsx diff --git a/components/shipping-table.tsx b/components/tables/shipping-table.tsx similarity index 100% rename from components/shipping-table.tsx rename to components/tables/shipping-table.tsx diff --git a/components/ui/aspect-ratio.tsx b/components/ui/aspect-ratio.tsx deleted file mode 100644 index d6a5226..0000000 --- a/components/ui/aspect-ratio.tsx +++ /dev/null @@ -1,7 +0,0 @@ -"use client" - -import * as AspectRatioPrimitive from "@radix-ui/react-aspect-ratio" - -const AspectRatio = AspectRatioPrimitive.Root - -export { AspectRatio } diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx index d6efcf9..73ae922 100644 --- a/components/ui/badge.tsx +++ b/components/ui/badge.tsx @@ -1,7 +1,7 @@ import * as React from "react"; import { cva, type VariantProps } from "class-variance-authority"; -import { cn } from "@/lib/utils"; +import { cn } from "@/lib/styles"; const badgeVariants = cva( "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", diff --git a/components/ui/button.tsx b/components/ui/button.tsx index 36496a2..3842446 100644 --- a/components/ui/button.tsx +++ b/components/ui/button.tsx @@ -2,7 +2,7 @@ import * as React from "react" import { Slot } from "@radix-ui/react-slot" import { cva, type VariantProps } from "class-variance-authority" -import { cn } from "@/lib/utils" +import { cn } from '@/lib/styles'; const buttonVariants = cva( "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", diff --git a/components/ui/card.tsx b/components/ui/card.tsx index f62edea..764892e 100644 --- a/components/ui/card.tsx +++ b/components/ui/card.tsx @@ -1,6 +1,6 @@ import * as React from "react" -import { cn } from "@/lib/utils" +import { cn } from "@/lib/styles"; const Card = React.forwardRef< HTMLDivElement, diff --git a/components/ui/checkbox.tsx b/components/ui/checkbox.tsx index df61a13..9cfca2c 100644 --- a/components/ui/checkbox.tsx +++ b/components/ui/checkbox.tsx @@ -4,7 +4,7 @@ import * as React from "react" import * as CheckboxPrimitive from "@radix-ui/react-checkbox" import { Check } from "lucide-react" -import { cn } from "@/lib/utils" +import { cn } from '@/lib/styles'; const Checkbox = React.forwardRef< React.ElementRef, diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx index 01ff19c..d20e012 100644 --- a/components/ui/dialog.tsx +++ b/components/ui/dialog.tsx @@ -4,7 +4,7 @@ import * as React from "react" import * as DialogPrimitive from "@radix-ui/react-dialog" import { X } from "lucide-react" -import { cn } from "@/lib/utils" +import { cn } from '@/lib/styles'; const Dialog = DialogPrimitive.Root diff --git a/components/ui/hover-card.tsx b/components/ui/hover-card.tsx deleted file mode 100644 index e54d91c..0000000 --- a/components/ui/hover-card.tsx +++ /dev/null @@ -1,29 +0,0 @@ -"use client" - -import * as React from "react" -import * as HoverCardPrimitive from "@radix-ui/react-hover-card" - -import { cn } from "@/lib/utils" - -const HoverCard = HoverCardPrimitive.Root - -const HoverCardTrigger = HoverCardPrimitive.Trigger - -const HoverCardContent = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( - -)) -HoverCardContent.displayName = HoverCardPrimitive.Content.displayName - -export { HoverCard, HoverCardTrigger, HoverCardContent } diff --git a/components/ui/input-otp.tsx b/components/ui/input-otp.tsx deleted file mode 100644 index f66fcfa..0000000 --- a/components/ui/input-otp.tsx +++ /dev/null @@ -1,71 +0,0 @@ -"use client" - -import * as React from "react" -import { OTPInput, OTPInputContext } from "input-otp" -import { Dot } from "lucide-react" - -import { cn } from "@/lib/utils" - -const InputOTP = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, containerClassName, ...props }, ref) => ( - -)) -InputOTP.displayName = "InputOTP" - -const InputOTPGroup = React.forwardRef< - React.ElementRef<"div">, - React.ComponentPropsWithoutRef<"div"> ->(({ className, ...props }, ref) => ( -
-)) -InputOTPGroup.displayName = "InputOTPGroup" - -const InputOTPSlot = React.forwardRef< - React.ElementRef<"div">, - React.ComponentPropsWithoutRef<"div"> & { index: number } ->(({ index, className, ...props }, ref) => { - const inputOTPContext = React.useContext(OTPInputContext) - const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index] - - return ( -
- {char} - {hasFakeCaret && ( -
-
-
- )} -
- ) -}) -InputOTPSlot.displayName = "InputOTPSlot" - -const InputOTPSeparator = React.forwardRef< - React.ElementRef<"div">, - React.ComponentPropsWithoutRef<"div"> ->(({ ...props }, ref) => ( -
- -
-)) -InputOTPSeparator.displayName = "InputOTPSeparator" - -export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator } diff --git a/components/ui/input.tsx b/components/ui/input.tsx index 68551b9..4579129 100644 --- a/components/ui/input.tsx +++ b/components/ui/input.tsx @@ -1,6 +1,6 @@ import * as React from "react" -import { cn } from "@/lib/utils" +import { cn } from '@/lib/styles'; const Input = React.forwardRef>( ({ className, type, ...props }, ref) => { diff --git a/components/ui/label.tsx b/components/ui/label.tsx index 5341821..ccc561d 100644 --- a/components/ui/label.tsx +++ b/components/ui/label.tsx @@ -4,7 +4,7 @@ import * as React from "react" import * as LabelPrimitive from "@radix-ui/react-label" import { cva, type VariantProps } from "class-variance-authority" -import { cn } from "@/lib/utils" +import { cn } from "@/lib/styles"; const labelVariants = cva( "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70" diff --git a/components/ui/select.tsx b/components/ui/select.tsx index cbe5a36..fc49086 100644 --- a/components/ui/select.tsx +++ b/components/ui/select.tsx @@ -4,7 +4,7 @@ import * as React from "react" import * as SelectPrimitive from "@radix-ui/react-select" import { Check, ChevronDown, ChevronUp } from "lucide-react" -import { cn } from "@/lib/utils" +import { cn } from '@/lib/styles'; const Select = SelectPrimitive.Root diff --git a/components/ui/skeleton.tsx b/components/ui/skeleton.tsx index 01b8b6d..c4281b2 100644 --- a/components/ui/skeleton.tsx +++ b/components/ui/skeleton.tsx @@ -1,4 +1,4 @@ -import { cn } from "@/lib/utils" +import { cn } from "@/lib/styles" function Skeleton({ className, diff --git a/components/ui/sonner.tsx b/components/ui/sonner.tsx deleted file mode 100644 index 452f4d9..0000000 --- a/components/ui/sonner.tsx +++ /dev/null @@ -1,31 +0,0 @@ -"use client" - -import { useTheme } from "next-themes" -import { Toaster as Sonner } from "sonner" - -type ToasterProps = React.ComponentProps - -const Toaster = ({ ...props }: ToasterProps) => { - const { theme = "system" } = useTheme() - - return ( - - ) -} - -export { Toaster } diff --git a/components/ui/table.tsx b/components/ui/table.tsx index 7f3502f..c8c6390 100644 --- a/components/ui/table.tsx +++ b/components/ui/table.tsx @@ -1,6 +1,6 @@ import * as React from "react" -import { cn } from "@/lib/utils" +import { cn } from '@/lib/styles'; const Table = React.forwardRef< HTMLTableElement, diff --git a/components/ui/textarea.tsx b/components/ui/textarea.tsx index 4d858bb..5ca2c05 100644 --- a/components/ui/textarea.tsx +++ b/components/ui/textarea.tsx @@ -1,6 +1,6 @@ import * as React from "react" -import { cn } from "@/lib/utils" +import { cn } from '@/lib/styles'; const Textarea = React.forwardRef< HTMLTextAreaElement, diff --git a/components/ui/toaster.tsx b/components/ui/toaster.tsx deleted file mode 100644 index 171beb4..0000000 --- a/components/ui/toaster.tsx +++ /dev/null @@ -1,35 +0,0 @@ -"use client" - -import { useToast } from "@/hooks/use-toast" -import { - Toast, - ToastClose, - ToastDescription, - ToastProvider, - ToastTitle, - ToastViewport, -} from "@/components/ui/toast" - -export function Toaster() { - const { toasts } = useToast() - - return ( - - {toasts.map(function ({ id, title, description, action, ...props }) { - return ( - -
- {title && {title}} - {description && ( - {description} - )} -
- {action} - -
- ) - })} - -
- ) -} diff --git a/components/ui/use-mobile.tsx b/components/ui/use-mobile.tsx deleted file mode 100644 index 2b0fe1d..0000000 --- a/components/ui/use-mobile.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import * as React from "react" - -const MOBILE_BREAKPOINT = 768 - -export function useIsMobile() { - const [isMobile, setIsMobile] = React.useState(undefined) - - React.useEffect(() => { - const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`) - const onChange = () => { - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) - } - mql.addEventListener("change", onChange) - setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) - return () => mql.removeEventListener("change", onChange) - }, []) - - return !!isMobile -} diff --git a/lib/client-utils.ts b/lib/client-utils.ts index 8b44940..8e993c7 100644 --- a/lib/client-utils.ts +++ b/lib/client-utils.ts @@ -1,46 +1,32 @@ /** - * Client-side API utilities with authentication handling + * Simple client-side fetch function for making API calls with Authorization header. */ - -const getAuthToken = (): string | null => { - const token = document.cookie - .split('; ') - .find(row => row.startsWith('Authorization=')) - ?.split('=')[1]; - - return token || null; // Return null instead of throwing an error -}; - -export const fetchClient = async ( - endpoint: string, - options: RequestInit = {} -): Promise => { +export async function clientFetch(url: string, options: RequestInit = {}): Promise { try { - const authToken = getAuthToken(); - if (!authToken) { - console.warn("No authentication token found. Redirecting to login..."); - window.location.href = "/login"; - return Promise.reject("Unauthorized"); - } + const authToken = document.cookie + .split('; ') + .find(row => row.startsWith('Authorization=')) + ?.split('=')[1] || localStorage.getItem('Authorization'); - const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}${endpoint}`, { - ...options, - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${authToken}`, - ...options.headers, - }, - credentials: 'include', - }); + console.log('authToken', authToken); - if (!res.ok) { - const errorData = await res.json(); - throw new Error(errorData.message || `Request failed: ${res.statusText}`); - } + // Merge Authorization header if token is found + const headers = { + 'Content-Type': 'application/json', + ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}), + ...options.headers, + }; - return res.json(); + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}${url}`, { + ...options, + headers, + }); + + if (!res.ok) throw new Error(`Request failed: ${res.statusText}`); + + return res.json(); } catch (error) { - console.error(`API request to ${endpoint} failed:`, error); - throw error; + console.error(`Client fetch error at ${url}:`, error); + throw error; } -}; \ No newline at end of file +} \ No newline at end of file diff --git a/lib/data-service.ts b/lib/data-service.ts index d62e4f0..04aa20b 100644 --- a/lib/data-service.ts +++ b/lib/data-service.ts @@ -1,31 +1,13 @@ -import { fetchClient } from './client-utils'; -import type { Product, ShippingMethod, ApiResponse } from './types'; - -export const ProductService = { - getAll: async (): Promise> => - fetchClient('/products'), - - create: async (product: Omit): Promise> => - fetchClient('/products', { method: 'POST', body: JSON.stringify(product) }), - - update: async (id: string, product: Partial): Promise> => - fetchClient(`/products/${id}`, { method: 'PUT', body: JSON.stringify(product) }), - - delete: async (id: string): Promise> => - fetchClient(`/products/${id}`, { method: 'DELETE' }) -}; - -// Shipping Operations -export const ShippingService = { - getAll: async (): Promise> => - fetchClient('/shipping-options'), - - create: async (method: Omit): Promise> => - fetchClient('/shipping-options', { method: 'POST', body: JSON.stringify(method) }), - - update: async (id: string, method: Partial): Promise> => - fetchClient(`/shipping-options/${id}`, { method: 'PUT', body: JSON.stringify(method) }), - - delete: async (id: string): Promise> => - fetchClient(`/shipping-options/${id}`, { method: 'DELETE' }) -}; \ No newline at end of file +/** + * Client-side fetch function for API requests. + */ +export async function fetchData(url: string, options: RequestInit = {}): Promise { + try { + const res = await fetch(url, options); + if (!res.ok) throw new Error(`Request failed: ${res.statusText}`); + return res.json(); + } catch (error) { + console.error(`Fetch error at ${url}:`, error); + throw error; + } +} \ No newline at end of file diff --git a/lib/fetchData.ts b/lib/fetchData.ts deleted file mode 100644 index 1a46f66..0000000 --- a/lib/fetchData.ts +++ /dev/null @@ -1,18 +0,0 @@ -export const fetchData = async (url: string, authToken: string) => { - try { - const response = await fetch(url, { - headers: { - Authorization: `Bearer ${authToken}`, - }, - credentials: "include", - }); - - if (!response.ok) throw new Error("Failed to fetch data"); - - return await response.json(); - } catch (error) { - console.error("Error fetching data:", error); - throw error; - } - }; - \ No newline at end of file diff --git a/lib/productData.ts b/lib/productData.ts index 9f61b36..6582c93 100644 --- a/lib/productData.ts +++ b/lib/productData.ts @@ -1,13 +1,11 @@ +import { fetchData } from '@/lib/data-service'; + export const fetchProductData = async (url: string, authToken: string) => { try { - const response = await fetch(url, { + return await fetchData(url, { headers: { Authorization: `Bearer ${authToken}` }, credentials: "include", }); - if (!response.ok) { - throw new Error("Failed to fetch product data"); - } - return await response.json(); } catch (error) { console.error("Error fetching product data:", error); throw error; @@ -21,7 +19,7 @@ export const saveProductData = async ( method: "POST" | "PUT" = "POST" ) => { try { - const response = await fetch(url, { + return await fetchData(url, { method, headers: { Authorization: `Bearer ${authToken}`, @@ -30,10 +28,6 @@ export const saveProductData = async ( credentials: "include", body: JSON.stringify(data), }); - if (!response.ok) { - throw new Error("Failed to save product data"); - } - return await response.json(); } catch (error) { console.error("Error saving product data:", error); throw error; @@ -42,7 +36,7 @@ export const saveProductData = async ( export const deleteProductData = async (url: string, authToken: string) => { try { - const response = await fetch(url, { + return await fetchData(url, { method: "DELETE", headers: { Authorization: `Bearer ${authToken}`, @@ -50,13 +44,8 @@ export const deleteProductData = async (url: string, authToken: string) => { }, credentials: "include", }); - - if (!response.ok) { - throw new Error("Failed to delete product data"); - } - return await response.json(); } catch (error) { console.error("Error deleting product data:", error); throw error; } -}; +}; \ No newline at end of file diff --git a/lib/server-utils.ts b/lib/server-service.ts similarity index 80% rename from lib/server-utils.ts rename to lib/server-service.ts index 719ec91..3593e5d 100644 --- a/lib/server-utils.ts +++ b/lib/server-service.ts @@ -2,14 +2,14 @@ import { cookies } from 'next/headers'; import { redirect } from 'next/navigation'; /** - * Server-side fetch wrapper with auth handling + * Server-side fetch wrapper with authentication. */ -export const fetchServer = async ( +export async function fetchServer( endpoint: string, options: RequestInit = {} -): Promise => { +): Promise { const cookieStore = cookies(); - const authToken = await cookieStore.get('Authorization')?.value; + const authToken = cookieStore.get('Authorization')?.value; if (!authToken) redirect('/login'); @@ -32,4 +32,4 @@ export const fetchServer = async ( console.error(`Server request to ${endpoint} failed:`, error); throw error; } -}; \ No newline at end of file +} \ No newline at end of file diff --git a/lib/shippingHelper.ts b/lib/shippingHelper.ts index 0044e2e..99838e1 100644 --- a/lib/shippingHelper.ts +++ b/lib/shippingHelper.ts @@ -1,6 +1,8 @@ +import { fetchData } from '@/lib/data-service'; + export const fetchShippingMethods = async (authToken: string) => { try { - const res = await fetch( + const res = await fetchData( `${process.env.NEXT_PUBLIC_API_URL}/shipping-options`, { headers: { @@ -11,8 +13,10 @@ export const fetchShippingMethods = async (authToken: string) => { } ); - if (!res.ok) throw new Error("Failed to fetch shipping options"); - return await res.json(); + console.log("Shipping Methods Response:", res); + + if (!res) throw new Error("Failed to fetch shipping options"); + return res } catch (error) { console.error("Error loading shipping options:", error); throw error; @@ -30,7 +34,7 @@ export const addShippingMethod = async ( newShipping: Omit ): Promise => { try { - const res = await fetch( + const res = await fetchData( `${process.env.NEXT_PUBLIC_API_URL}/shipping-options`, { method: "POST", @@ -61,7 +65,7 @@ export const addShippingMethod = async ( export const deleteShippingMethod = async (authToken: string, id: string) => { try { - const res = await fetch( + const res = await fetchData( `${process.env.NEXT_PUBLIC_API_URL}/shipping-options/${id}`, { method: "DELETE", @@ -71,7 +75,6 @@ export const deleteShippingMethod = async (authToken: string, id: string) => { ); if (!res.ok) throw new Error("Failed to delete shipping method"); - // Since there is no content, just return success status. return { success: res.status === 204 }; } catch (error) { console.error("Error deleting shipping method:", error); @@ -85,7 +88,7 @@ export const updateShippingMethod = async ( updatedShipping: any ) => { try { - const res = await fetch( + const res = await fetchData( `${process.env.NEXT_PUBLIC_API_URL}/shipping-options/${id}`, { method: "PUT", @@ -98,10 +101,10 @@ export const updateShippingMethod = async ( } ); - if (!res.ok) throw new Error("Failed to update shipping method"); - return await res.json(); + if (!res) throw new Error("Failed to update shipping method"); + return res } catch (error) { console.error("Error updating shipping method:", error); throw error; } -}; +}; \ No newline at end of file diff --git a/lib/storeHelper.ts b/lib/storeHelper.ts index 1cbb474..d927403 100644 --- a/lib/storeHelper.ts +++ b/lib/storeHelper.ts @@ -1,3 +1,5 @@ +import { fetchData } from '@/lib/data-service'; + export const apiRequest = async (endpoint: string, method: string = "GET", body?: T | null) => { try { if (typeof document === "undefined") { @@ -10,7 +12,6 @@ export const apiRequest = async (endpoint: string, method: string = "GE ?.split("=")[1]; if (!authToken){ - // go to /login document.location.href = "/login"; throw new Error("No authentication token found"); } @@ -32,16 +33,16 @@ export const apiRequest = async (endpoint: string, method: string = "GE const API_URL = process.env.NEXT_PUBLIC_API_URL; if (!API_URL) throw new Error("NEXT_PUBLIC_API_URL is not set in environment variables"); - const res = await fetch(`${API_URL}${endpoint}`, options); + const res = await fetchData(`${API_URL}${endpoint}`, options); - if (!res.ok) { + if (!res) { const errorResponse = await res.json().catch(() => null); const errorMessage = errorResponse?.error || res.statusText || "Unknown error"; throw new Error(`Failed to ${method} ${endpoint}: ${errorMessage}`); } // ✅ Return JSON response - return await res.json(); + return res; } catch (error: unknown) { if (error instanceof Error) { console.error(`🚨 API Request Error: ${error.message}`); diff --git a/lib/utils.ts b/lib/styles.ts similarity index 100% rename from lib/utils.ts rename to lib/styles.ts diff --git a/app/fonts/GeistMonoVF.woff b/public/fonts/GeistMonoVF.woff similarity index 100% rename from app/fonts/GeistMonoVF.woff rename to public/fonts/GeistMonoVF.woff diff --git a/app/fonts/GeistVF.woff b/public/fonts/GeistVF.woff similarity index 100% rename from app/fonts/GeistVF.woff rename to public/fonts/GeistVF.woff diff --git a/tailwind.config.js b/tailwind.config.js deleted file mode 100644 index db1dd71..0000000 --- a/tailwind.config.js +++ /dev/null @@ -1,50 +0,0 @@ -module.exports = { - darkMode: ["class"], - content: ["./pages/**/*.{ts,tsx}", "./components/**/*.{ts,tsx}", "./app/**/*.{ts,tsx}", "./src/**/*.{ts,tsx}"], - theme: { - extend: { - colors: { - border: "hsl(var(--border))", - input: "hsl(var(--input))", - ring: "hsl(var(--ring))", - background: "hsl(var(--background))", - foreground: "hsl(var(--foreground))", - primary: { - DEFAULT: "hsl(var(--primary))", - foreground: "hsl(var(--primary-foreground))", - }, - secondary: { - DEFAULT: "hsl(var(--secondary))", - foreground: "hsl(var(--secondary-foreground))", - }, - destructive: { - DEFAULT: "hsl(var(--destructive))", - foreground: "hsl(var(--destructive-foreground))", - }, - muted: { - DEFAULT: "hsl(var(--muted))", - foreground: "hsl(var(--muted-foreground))", - }, - accent: { - DEFAULT: "hsl(var(--accent))", - foreground: "hsl(var(--accent-foreground))", - }, - popover: { - DEFAULT: "hsl(var(--popover))", - foreground: "hsl(var(--popover-foreground))", - }, - card: { - DEFAULT: "hsl(var(--card))", - foreground: "hsl(var(--card-foreground))", - }, - }, - borderRadius: { - lg: "var(--radius)", - md: "calc(var(--radius) - 2px)", - sm: "calc(var(--radius) - 4px)", - }, - }, - }, - plugins: [require("tailwindcss-animate")], -} -