diff --git a/app/auth/login/components/LoginForm.tsx b/app/auth/login/components/LoginForm.tsx
new file mode 100644
index 0000000..5fee7f5
--- /dev/null
+++ b/app/auth/login/components/LoginForm.tsx
@@ -0,0 +1,134 @@
+"use client"
+
+import { useState, useEffect } 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 { toast } from "sonner";
+
+export default function LoginForm() {
+ const [username, setUsername] = useState("");
+ const [password, setPassword] = useState("");
+ const [isLoading, setIsLoading] = useState(false);
+ const router = useRouter();
+ const searchParams = useSearchParams();
+ const redirectUrl = searchParams.get("redirectUrl") || "/dashboard";
+
+ // Check if already logged in
+ useEffect(() => {
+ const authToken = document.cookie
+ .split("; ")
+ .find((row) => row.startsWith("Authorization="))
+ ?.split("=")[1];
+
+ if (authToken) {
+ router.push("/dashboard");
+ }
+ }, [router]);
+
+ async function handleLogin(e: React.FormEvent) {
+ e.preventDefault();
+ setIsLoading(true);
+
+ try {
+ // Using fetch directly with the proxy path
+ const response = await fetch("/api/auth/login", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ username, password }),
+ });
+
+ let data;
+ try {
+ data = await response.json();
+ } catch (parseError) {
+ console.error("Login parse error:", parseError);
+ toast.error("Server Error", {
+ description: "The server response couldn't be processed. Please try again."
+ });
+ setIsLoading(false);
+ return;
+ }
+
+ if (response.ok && data.token) {
+ // Store the token in both cookie and localStorage for redundancy
+ document.cookie = `Authorization=${data.token}; path=/; Secure; SameSite=Strict; max-age=604800`;
+ localStorage.setItem("Authorization", data.token);
+
+ toast.success("Login successful");
+
+ // Redirect to dashboard or the original redirect URL
+ router.push(redirectUrl);
+ } else {
+ // Handle HTTP error responses
+ const errorMessage = data.error || data.message || data.details || "Invalid credentials";
+ toast.error("Login Failed", {
+ description: errorMessage,
+ });
+ console.error("Login error response:", { status: response.status, data });
+ }
+ } catch (error) {
+ toast.error("Connection Error", {
+ description: "Unable to connect to the server. Please check your internet connection and try again.",
+ });
+ console.error("Login network error:", error);
+ } finally {
+ setIsLoading(false);
+ }
+ }
+
+ return (
+
+
+
+
Welcome back
+
Please sign in to your account
+
+
+
+
+
+ Don't have an account?{" "}
+
+ Sign up
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/app/auth/login/page.tsx b/app/auth/login/page.tsx
index 088cbe5..6e53e21 100644
--- a/app/auth/login/page.tsx
+++ b/app/auth/login/page.tsx
@@ -1,138 +1,12 @@
"use client"
-import { useState, useEffect, Suspense } from "react";
-import { useRouter, useSearchParams } from "next/navigation";
-import Link from "next/link";
+import React, { Suspense, lazy } from "react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
-import { toast } from "sonner";
-// Separate the main login functionality into a client component
-function LoginForm() {
- const [username, setUsername] = useState("");
- const [password, setPassword] = useState("");
- const [isLoading, setIsLoading] = useState(false);
- const router = useRouter();
- const searchParams = useSearchParams();
- const redirectUrl = searchParams.get("redirectUrl") || "/dashboard";
-
- // Check if already logged in
- useEffect(() => {
- const authToken = document.cookie
- .split("; ")
- .find((row) => row.startsWith("Authorization="))
- ?.split("=")[1];
-
- if (authToken) {
- router.push("/dashboard");
- }
- }, [router]);
-
- async function handleLogin(e: React.FormEvent) {
- e.preventDefault();
- setIsLoading(true);
-
- try {
- // Using fetch directly with the proxy path
- const response = await fetch("/api/auth/login", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({ username, password }),
- });
-
- let data;
- try {
- data = await response.json();
- } catch (parseError) {
- console.error("Login parse error:", parseError);
- toast.error("Server Error", {
- description: "The server response couldn't be processed. Please try again."
- });
- setIsLoading(false);
- return;
- }
-
- if (response.ok && data.token) {
- // Store the token in both cookie and localStorage for redundancy
- document.cookie = `Authorization=${data.token}; path=/; Secure; SameSite=Strict; max-age=604800`;
- localStorage.setItem("Authorization", data.token);
-
- toast.success("Login successful");
-
- // Redirect to dashboard or the original redirect URL
- router.push(redirectUrl);
- } else {
- // Handle HTTP error responses
- const errorMessage = data.error || data.message || data.details || "Invalid credentials";
- toast.error("Login Failed", {
- description: errorMessage,
- });
- console.error("Login error response:", { status: response.status, data });
- }
- } catch (error) {
- toast.error("Connection Error", {
- description: "Unable to connect to the server. Please check your internet connection and try again.",
- });
- console.error("Login network error:", error);
- } finally {
- setIsLoading(false);
- }
- }
-
- return (
-
-
-
-
Welcome back
-
Please sign in to your account
-
-
-
-
-
- Don't have an account?{" "}
-
- Sign up
-
-
-
-
- );
-}
+// Use lazy loading for the form component
+const LoginForm = lazy(() => import('./components/LoginForm'));
// Simple loading state for the Suspense boundary
function LoginLoading() {
diff --git a/components/notifications/OrderNotifications.tsx b/components/notifications/OrderNotifications.tsx
index 31d74e3..c4a1830 100644
--- a/components/notifications/OrderNotifications.tsx
+++ b/components/notifications/OrderNotifications.tsx
@@ -75,8 +75,13 @@ export default function OrderNotifications() {
const checkForNewOrders = async () => {
try {
- // Fetch orders from the last 24 hours that are in paid status
- const orderData = await clientFetch("/orders?status=paid&limit=10");
+ // Get orders from the last 24 hours with a more efficient query
+ const yesterday = new Date();
+ yesterday.setDate(yesterday.getDate() - 1);
+ const timestamp = yesterday.toISOString();
+
+ // Include timestamp filter to reduce load
+ const orderData = await clientFetch(`/orders?status=paid&limit=10&after=${timestamp}`);
const orders: Order[] = orderData.orders || [];
// If this is the first fetch, just store the orders without notifications
diff --git a/components/notifications/UnifiedNotifications.tsx b/components/notifications/UnifiedNotifications.tsx
index 71e4358..45d4dd6 100644
--- a/components/notifications/UnifiedNotifications.tsx
+++ b/components/notifications/UnifiedNotifications.tsx
@@ -102,8 +102,13 @@ export default function UnifiedNotifications() {
const checkForNewOrders = async () => {
try {
- // Fetch orders from the last 24 hours that are in paid status
- const orderData = await clientFetch("/orders?status=paid&limit=10");
+ // Get orders from the last 24 hours with a more efficient query
+ const yesterday = new Date();
+ yesterday.setDate(yesterday.getDate() - 1);
+ const timestamp = yesterday.toISOString();
+
+ // Include timestamp filter to reduce load
+ const orderData = await clientFetch(`/orders?status=paid&limit=10&after=${timestamp}`);
const orders: Order[] = orderData.orders || [];
// If this is the first fetch, just store the orders without notifications
diff --git a/package-lock.json b/package-lock.json
index d719f7e..50a4242 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -68,6 +68,7 @@
"@types/node": "^22",
"@types/react": "^18",
"@types/react-dom": "^18",
+ "cross-env": "^7.0.3",
"eslint": "^9.19.0",
"postcss": "^8",
"tailwindcss": "^3.4.17",
@@ -2700,6 +2701,25 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/cross-env": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
+ "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.1"
+ },
+ "bin": {
+ "cross-env": "src/bin/cross-env.js",
+ "cross-env-shell": "src/bin/cross-env-shell.js"
+ },
+ "engines": {
+ "node": ">=10.14",
+ "npm": ">=6",
+ "yarn": ">=1"
+ }
+ },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
diff --git a/package.json b/package.json
index b253692..32aefe2 100644
--- a/package.json
+++ b/package.json
@@ -4,9 +4,12 @@
"private": true,
"scripts": {
"dev": "next dev",
- "build": "next build",
+ "build": "cross-env NODE_OPTIONS='--max_old_space_size=4096' NEXT_TELEMETRY_DISABLED=1 next build",
+ "build:fast": "cross-env NODE_OPTIONS='--max_old_space_size=4096' NEXT_TELEMETRY_DISABLED=1 NEXT_SKIP_LINT=1 NEXT_SKIP_TS_CHECK=1 next build",
+ "build:optimized": "node scripts/optimize-build.js",
"start": "next start",
- "lint": "next lint"
+ "lint": "next lint",
+ "clean": "rm -rf .next && rm -rf node_modules/.cache"
},
"dependencies": {
"@hookform/resolvers": "^3.9.1",
@@ -69,6 +72,7 @@
"@types/node": "^22",
"@types/react": "^18",
"@types/react-dom": "^18",
+ "cross-env": "^7.0.3",
"eslint": "^9.19.0",
"postcss": "^8",
"tailwindcss": "^3.4.17",