From 8d7d9b9e1c71b98b843a04ac30472dc86e5ec17a Mon Sep 17 00:00:00 2001 From: NotII <46204250+NotII@users.noreply.github.com> Date: Sun, 23 Mar 2025 21:25:37 +0000 Subject: [PATCH] cleanup --- app/dashboard/products/page.tsx | 118 ++++++++++++-------------------- components/KeepOnline.ts | 2 +- lib/client-utils.ts | 11 ++- lib/server-service.ts | 16 ++++- middleware.ts | 7 +- 5 files changed, 74 insertions(+), 80 deletions(-) diff --git a/app/dashboard/products/page.tsx b/app/dashboard/products/page.tsx index fa00d59..4c12407 100644 --- a/app/dashboard/products/page.tsx +++ b/app/dashboard/products/page.tsx @@ -8,11 +8,11 @@ import { Input } from "@/components/ui/input"; import { Product } from "@/models/products"; import { Plus, Upload, Search, RefreshCw } from "lucide-react"; import { - fetchProductData, saveProductData, saveProductImage, deleteProductData, } from "@/lib/productData"; +import { clientFetch } from "@/lib/client-utils"; import { ProductModal } from "@/components/modals/product-modal"; import ProductTable from "@/components/tables/product-table"; import { Category } from "@/models/categories"; @@ -55,12 +55,9 @@ export default function ProductsPage() { try { setLoading(true); - const productsUrl = `${process.env.NEXT_PUBLIC_API_URL}/products`; - const categoriesUrl = `${process.env.NEXT_PUBLIC_API_URL}/categories`; - const [fetchedProducts, fetchedCategories] = await Promise.all([ - fetchProductData(productsUrl, authToken), - fetchProductData(categoriesUrl, authToken), + clientFetch('/products'), + clientFetch('/categories'), ]); console.log("Fetched Products:", fetchedProducts); @@ -76,6 +73,7 @@ export default function ProductsPage() { setLoading(false); } catch (error) { console.error("Error fetching data:", error); + toast.error("Failed to load products"); setLoading(false); } }; @@ -115,67 +113,41 @@ export default function ProductsPage() { const handleSaveProduct = async (data: Product, file?: File | null) => { - const authToken = document.cookie - .split("; ") - .find((row) => row.startsWith("Authorization=")) - ?.split("=")[1]; - - if (!authToken) { - router.push("/login"); - return; - } - - setLoading(true); - try { - const url = editing - ? `${process.env.NEXT_PUBLIC_API_URL}/products/${data._id}` - : `${process.env.NEXT_PUBLIC_API_URL}/products`; + setLoading(true); - // Save product data - const savedProduct = await saveProductData( - url, - { - name: data.name, - description: data.description, - unitType: data.unitType, - category: data.category, - pricing: data.pricing, - stockTracking: data.stockTracking, - currentStock: data.currentStock, - lowStockThreshold: data.lowStockThreshold - }, - authToken, - editing ? "PUT" : "POST" - ); - - if (file) { - const imageUrl = `${process.env.NEXT_PUBLIC_API_URL}/products/${savedProduct._id}/image`; - await saveProductImage( - imageUrl, - file, - authToken - ); + if (editing && !data._id) { + throw new Error("Cannot update product without an ID"); } - // If editing and stock values were updated, update stock in the dedicated endpoint - if (editing && data.stockTracking !== undefined) { - const stockUrl = `${process.env.NEXT_PUBLIC_API_URL}/stock/${data._id}`; - await saveProductData( - stockUrl, - { - stockTracking: data.stockTracking, - currentStock: data.currentStock || 0, - lowStockThreshold: data.lowStockThreshold || 10 - }, - authToken, - "PUT" - ); + // Save the product data + const endpoint = editing ? `/products/${data._id}` : "/products"; + const method = editing ? "PUT" : "POST"; + + const productResponse = await clientFetch(endpoint, { + method, + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(data), + }); + + // If there's a new image to upload + if (file) { + const imageEndpoint = `/products/${productResponse._id || data._id}/image`; + + const formData = new FormData(); + formData.append("file", file); + + await clientFetch(imageEndpoint, { + method: "PUT", + body: formData, + headers: {}, // Let the browser set the content-type for FormData + }); } // Refresh products list - const productsUrl = `${process.env.NEXT_PUBLIC_API_URL}/products`; - const fetchedProducts = await fetchProductData(productsUrl, authToken); + const fetchedProducts = await clientFetch('/products'); setProducts(fetchedProducts); setModalOpen(false); @@ -195,25 +167,23 @@ export default function ProductsPage() { const handleDeleteProduct = async (productId: string) => { if (!confirm("Are you sure you want to delete this product?")) return; - const authToken = document.cookie - .split("; ") - .find((row) => row.startsWith("Authorization=")) - ?.split("=")[1]; - - if (!authToken) { - router.push("/login"); - return; - } - try { - const url = `${process.env.NEXT_PUBLIC_API_URL}/products/${productId}`; - await deleteProductData(url, authToken); + setLoading(true); + + await clientFetch(`/products/${productId}`, { + method: "DELETE", + }); + + // Refresh products list + const fetchedProducts = await clientFetch('/products'); + setProducts(fetchedProducts); - setProducts(products.filter((p) => p._id !== productId)); toast.success("Product deleted successfully"); + setLoading(false); } catch (error) { - console.error("Error deleting product:", error); + console.error(error); toast.error("Failed to delete product"); + setLoading(false); } }; diff --git a/components/KeepOnline.ts b/components/KeepOnline.ts index b4dc4ce..4989ad8 100644 --- a/components/KeepOnline.ts +++ b/components/KeepOnline.ts @@ -8,7 +8,7 @@ const KeepOnline = () => { if(window.location.pathname.includes("/dashboard")){ const updateOnlineStatus = () => { console.log("Updating online status..."); - clientFetch("/auth/me"); + clientFetch('/auth/me'); } updateOnlineStatus(); diff --git a/lib/client-utils.ts b/lib/client-utils.ts index bb39467..dec31df 100644 --- a/lib/client-utils.ts +++ b/lib/client-utils.ts @@ -14,7 +14,16 @@ export async function clientFetch(url: string, options: RequestInit = {}): Promi ...options.headers, }; - const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}${url}`, { + // Ensure the url doesn't start with a slash if it's going to be appended to a URL that ends with one + const cleanUrl = url.startsWith('/') ? url.substring(1) : url; + const baseUrl = process.env.NEXT_PUBLIC_API_URL || '/api'; + + // Ensure there's only one slash between the base URL and endpoint + const fullUrl = baseUrl.endsWith('/') + ? `${baseUrl}${cleanUrl}` + : `${baseUrl}/${cleanUrl}`; + + const res = await fetch(fullUrl, { ...options, headers, }); diff --git a/lib/server-service.ts b/lib/server-service.ts index 26e27d5..499da21 100644 --- a/lib/server-service.ts +++ b/lib/server-service.ts @@ -13,9 +13,21 @@ export async function fetchServer( if (!authToken) redirect('/login'); + // Ensure the endpoint doesn't start with a slash if it's going to be appended to a URL that ends with one + const cleanEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint; + try { - console.log(`${endpoint}`) - const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}${endpoint}`, { + // Make sure we're using a complete URL (protocol + hostname + path) + const apiUrl = process.env.SERVER_API_URL || 'https://internal-api.inboxi.ng/api'; + + // Ensure there's only one slash between the base URL and endpoint + const url = apiUrl.endsWith('/') + ? `${apiUrl}${cleanEndpoint}` + : `${apiUrl}/${cleanEndpoint}`; + + console.log(`Making server request to: ${url}`); + + const res = await fetch(url, { ...options, headers: { 'Content-Type': 'application/json', diff --git a/middleware.ts b/middleware.ts index f285bed..d0ffe71 100644 --- a/middleware.ts +++ b/middleware.ts @@ -10,8 +10,11 @@ export async function middleware(req: NextRequest) { } try { - const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/auth/me`, { - method: "GET", + // Need to use a full URL for server-side fetch + const baseUrl = req.nextUrl.origin + '/api'; + + const res = await fetch(`${baseUrl}/auth/me`, { + method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`,