cleanup
This commit is contained in:
@@ -8,11 +8,11 @@ import { Input } from "@/components/ui/input";
|
|||||||
import { Product } from "@/models/products";
|
import { Product } from "@/models/products";
|
||||||
import { Plus, Upload, Search, RefreshCw } from "lucide-react";
|
import { Plus, Upload, Search, RefreshCw } from "lucide-react";
|
||||||
import {
|
import {
|
||||||
fetchProductData,
|
|
||||||
saveProductData,
|
saveProductData,
|
||||||
saveProductImage,
|
saveProductImage,
|
||||||
deleteProductData,
|
deleteProductData,
|
||||||
} from "@/lib/productData";
|
} from "@/lib/productData";
|
||||||
|
import { clientFetch } from "@/lib/client-utils";
|
||||||
import { ProductModal } from "@/components/modals/product-modal";
|
import { ProductModal } from "@/components/modals/product-modal";
|
||||||
import ProductTable from "@/components/tables/product-table";
|
import ProductTable from "@/components/tables/product-table";
|
||||||
import { Category } from "@/models/categories";
|
import { Category } from "@/models/categories";
|
||||||
@@ -55,12 +55,9 @@ export default function ProductsPage() {
|
|||||||
try {
|
try {
|
||||||
setLoading(true);
|
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([
|
const [fetchedProducts, fetchedCategories] = await Promise.all([
|
||||||
fetchProductData(productsUrl, authToken),
|
clientFetch('/products'),
|
||||||
fetchProductData(categoriesUrl, authToken),
|
clientFetch('/categories'),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
console.log("Fetched Products:", fetchedProducts);
|
console.log("Fetched Products:", fetchedProducts);
|
||||||
@@ -76,6 +73,7 @@ export default function ProductsPage() {
|
|||||||
setLoading(false);
|
setLoading(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error fetching data:", error);
|
console.error("Error fetching data:", error);
|
||||||
|
toast.error("Failed to load products");
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -115,67 +113,41 @@ export default function ProductsPage() {
|
|||||||
|
|
||||||
|
|
||||||
const handleSaveProduct = async (data: Product, file?: File | null) => {
|
const handleSaveProduct = async (data: Product, file?: File | null) => {
|
||||||
const authToken = document.cookie
|
try {
|
||||||
.split("; ")
|
|
||||||
.find((row) => row.startsWith("Authorization="))
|
|
||||||
?.split("=")[1];
|
|
||||||
|
|
||||||
if (!authToken) {
|
|
||||||
router.push("/login");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
|
||||||
try {
|
if (editing && !data._id) {
|
||||||
const url = editing
|
throw new Error("Cannot update product without an ID");
|
||||||
? `${process.env.NEXT_PUBLIC_API_URL}/products/${data._id}`
|
|
||||||
: `${process.env.NEXT_PUBLIC_API_URL}/products`;
|
|
||||||
|
|
||||||
// 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 and stock values were updated, update stock in the dedicated endpoint
|
// Save the product data
|
||||||
if (editing && data.stockTracking !== undefined) {
|
const endpoint = editing ? `/products/${data._id}` : "/products";
|
||||||
const stockUrl = `${process.env.NEXT_PUBLIC_API_URL}/stock/${data._id}`;
|
const method = editing ? "PUT" : "POST";
|
||||||
await saveProductData(
|
|
||||||
stockUrl,
|
const productResponse = await clientFetch(endpoint, {
|
||||||
{
|
method,
|
||||||
stockTracking: data.stockTracking,
|
headers: {
|
||||||
currentStock: data.currentStock || 0,
|
"Content-Type": "application/json",
|
||||||
lowStockThreshold: data.lowStockThreshold || 10
|
|
||||||
},
|
},
|
||||||
authToken,
|
body: JSON.stringify(data),
|
||||||
"PUT"
|
});
|
||||||
);
|
|
||||||
|
// 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
|
// Refresh products list
|
||||||
const productsUrl = `${process.env.NEXT_PUBLIC_API_URL}/products`;
|
const fetchedProducts = await clientFetch('/products');
|
||||||
const fetchedProducts = await fetchProductData(productsUrl, authToken);
|
|
||||||
setProducts(fetchedProducts);
|
setProducts(fetchedProducts);
|
||||||
|
|
||||||
setModalOpen(false);
|
setModalOpen(false);
|
||||||
@@ -195,25 +167,23 @@ export default function ProductsPage() {
|
|||||||
const handleDeleteProduct = async (productId: string) => {
|
const handleDeleteProduct = async (productId: string) => {
|
||||||
if (!confirm("Are you sure you want to delete this product?")) return;
|
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 {
|
try {
|
||||||
const url = `${process.env.NEXT_PUBLIC_API_URL}/products/${productId}`;
|
setLoading(true);
|
||||||
await deleteProductData(url, authToken);
|
|
||||||
|
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");
|
toast.success("Product deleted successfully");
|
||||||
|
setLoading(false);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error deleting product:", error);
|
console.error(error);
|
||||||
toast.error("Failed to delete product");
|
toast.error("Failed to delete product");
|
||||||
|
setLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ const KeepOnline = () => {
|
|||||||
if(window.location.pathname.includes("/dashboard")){
|
if(window.location.pathname.includes("/dashboard")){
|
||||||
const updateOnlineStatus = () => {
|
const updateOnlineStatus = () => {
|
||||||
console.log("Updating online status...");
|
console.log("Updating online status...");
|
||||||
clientFetch("/auth/me");
|
clientFetch('/auth/me');
|
||||||
}
|
}
|
||||||
|
|
||||||
updateOnlineStatus();
|
updateOnlineStatus();
|
||||||
|
|||||||
@@ -14,7 +14,16 @@ export async function clientFetch(url: string, options: RequestInit = {}): Promi
|
|||||||
...options.headers,
|
...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,
|
...options,
|
||||||
headers,
|
headers,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -13,9 +13,21 @@ export async function fetchServer<T = unknown>(
|
|||||||
|
|
||||||
if (!authToken) redirect('/login');
|
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 {
|
try {
|
||||||
console.log(`${endpoint}`)
|
// Make sure we're using a complete URL (protocol + hostname + path)
|
||||||
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}${endpoint}`, {
|
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,
|
...options,
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
|
|||||||
@@ -10,7 +10,10 @@ export async function middleware(req: NextRequest) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/auth/me`, {
|
// 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",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|||||||
Reference in New Issue
Block a user