From edcd0c1e06a95fb9551a751d85a8ca071ea0f8eb Mon Sep 17 00:00:00 2001 From: NotII <46204250+NotII@users.noreply.github.com> Date: Mon, 24 Mar 2025 13:43:42 +0000 Subject: [PATCH 1/3] erm what the sigma --- app/auth/login/components/LoginForm.tsx | 58 +++++++++--- app/dashboard/chats/[id]/loading.tsx | 100 +++++++++++++++++++++ app/dashboard/chats/loading.tsx | 15 ++++ app/dashboard/loading.tsx | 78 ++++++++++++++++ app/dashboard/orders/[id]/loading.tsx | 105 ++++++++++++++++++++++ app/dashboard/orders/loading.tsx | 15 ++++ app/dashboard/products/loading.tsx | 15 ++++ components/dashboard/page-loading.tsx | 114 ++++++++++++++++++++++++ lib/api-utils.ts | 69 ++++++++++++++ lib/client-utils.ts | 6 -- next.config.mjs | 3 + 11 files changed, 562 insertions(+), 16 deletions(-) create mode 100644 app/dashboard/chats/[id]/loading.tsx create mode 100644 app/dashboard/chats/loading.tsx create mode 100644 app/dashboard/loading.tsx create mode 100644 app/dashboard/orders/[id]/loading.tsx create mode 100644 app/dashboard/orders/loading.tsx create mode 100644 app/dashboard/products/loading.tsx create mode 100644 components/dashboard/page-loading.tsx diff --git a/app/auth/login/components/LoginForm.tsx b/app/auth/login/components/LoginForm.tsx index 5fee7f5..1cd4894 100644 --- a/app/auth/login/components/LoginForm.tsx +++ b/app/auth/login/components/LoginForm.tsx @@ -1,17 +1,20 @@ "use client" -import { useState, useEffect } from "react"; +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 { toast } from "sonner"; +import { Loader2 } from "lucide-react"; export default function LoginForm() { const [username, setUsername] = useState(""); const [password, setPassword] = useState(""); const [isLoading, setIsLoading] = useState(false); + const [loginSuccess, setLoginSuccess] = useState(false); + const redirectTimeoutRef = useRef(null); const router = useRouter(); const searchParams = useSearchParams(); const redirectUrl = searchParams.get("redirectUrl") || "/dashboard"; @@ -28,6 +31,15 @@ export default function LoginForm() { } }, [router]); + // Cleanup timeout on unmount + useEffect(() => { + return () => { + if (redirectTimeoutRef.current) { + clearTimeout(redirectTimeoutRef.current); + } + }; + }, []); + async function handleLogin(e: React.FormEvent) { e.preventDefault(); setIsLoading(true); @@ -57,10 +69,19 @@ export default function LoginForm() { document.cookie = `Authorization=${data.token}; path=/; Secure; SameSite=Strict; max-age=604800`; localStorage.setItem("Authorization", data.token); + // Show success notification toast.success("Login successful"); - // Redirect to dashboard or the original redirect URL + // Set success state for animation + setLoginSuccess(true); + + // Try Next.js router navigation router.push(redirectUrl); + + // Set up a fallback manual redirect if Next.js navigation doesn't work + redirectTimeoutRef.current = setTimeout(() => { + window.location.href = redirectUrl; + }, 1500); // Wait 1.5 seconds before trying manual redirect } else { // Handle HTTP error responses const errorMessage = data.error || data.message || data.details || "Invalid credentials"; @@ -68,20 +89,20 @@ export default function LoginForm() { description: errorMessage, }); console.error("Login error response:", { status: response.status, data }); + setIsLoading(false); } } 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

@@ -89,7 +110,7 @@ export default function LoginForm() {
-
+
setUsername(e.target.value)} className="mt-1" + disabled={isLoading || loginSuccess} />
-
+
setPassword(e.target.value)} className="mt-1" + disabled={isLoading || loginSuccess} />
- -

+

Don't have an account?{" "} Sign up diff --git a/app/dashboard/chats/[id]/loading.tsx b/app/dashboard/chats/[id]/loading.tsx new file mode 100644 index 0000000..6d48584 --- /dev/null +++ b/app/dashboard/chats/[id]/loading.tsx @@ -0,0 +1,100 @@ +import Layout from "@/components/layout/layout"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Skeleton } from "@/components/ui/skeleton"; +import { Loader2 } from "lucide-react"; + +export default function ChatDetailLoading() { + return ( + +

+ {/* Header */} +
+
+ + +
+ +
+ + {/* Chat window */} +
+ {/* Chat messages area */} +
+
+
+ +

Loading messages...

+
+
+ +
+ {/* Customer messages */} +
+
+
+ + +
+
+ +
+
+
+ + {/* Vendor messages */} +
+
+
+ + +
+
+ + +
+
+
+ + {/* Customer messages */} +
+
+
+ + +
+
+ + +
+
+
+ + {/* Vendor messages */} +
+
+
+ + +
+
+ + + +
+
+
+
+
+ + {/* Chat input area */} +
+
+ + +
+
+
+
+ + ); +} \ No newline at end of file diff --git a/app/dashboard/chats/loading.tsx b/app/dashboard/chats/loading.tsx new file mode 100644 index 0000000..a98b878 --- /dev/null +++ b/app/dashboard/chats/loading.tsx @@ -0,0 +1,15 @@ +import Layout from "@/components/layout/layout"; +import PageLoading from "@/components/dashboard/page-loading"; + +export default function ChatsLoading() { + return ( + + + + ); +} \ No newline at end of file diff --git a/app/dashboard/loading.tsx b/app/dashboard/loading.tsx new file mode 100644 index 0000000..2f28d14 --- /dev/null +++ b/app/dashboard/loading.tsx @@ -0,0 +1,78 @@ +"use client" + +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Skeleton } from "@/components/ui/skeleton"; +import Layout from "@/components/layout/layout"; +import { Loader2 } from "lucide-react"; + +export default function DashboardLoading() { + return ( + +
+ {/* Header skeleton with greeting & quote */} +
+ + +
+ + {/* Order statistics skeletons */} +
+ {[...Array(4)].map((_, i) => ( + + + + + +
+ + + + +
+
+
+ ))} +
+ + {/* Best selling products skeleton */} + +
+
+ +

Loading dashboard data...

+
+
+ + +
+ + + + + + +
+
+ + +
+ {[...Array(5)].map((_, i) => ( +
+ +
+ + +
+
+ + +
+
+ ))} +
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/app/dashboard/orders/[id]/loading.tsx b/app/dashboard/orders/[id]/loading.tsx new file mode 100644 index 0000000..ebf4d80 --- /dev/null +++ b/app/dashboard/orders/[id]/loading.tsx @@ -0,0 +1,105 @@ +import Layout from "@/components/layout/layout"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Skeleton } from "@/components/ui/skeleton"; +import { Loader2 } from "lucide-react"; + +export default function OrderDetailLoading() { + return ( + +
+ {/* Header */} +
+
+ + +
+ +
+ +
+ {/* Order Info Card */} + + + + + + + + {[...Array(6)].map((_, i) => ( +
+ + +
+ ))} +
+
+ + {/* Customer Info Card */} + + + + + + + + {[...Array(4)].map((_, i) => ( +
+ + +
+ ))} +
+
+ + {/* Order Items Card */} + +
+
+ +

Loading order details...

+
+
+ + + + + + + +
+ {[...Array(3)].map((_, i) => ( +
+ +
+ + +
+
+ + +
+
+ ))} +
+ +
+
+ {[...Array(3)].map((_, i) => ( +
+ + +
+ ))} +
+ + +
+
+
+
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/app/dashboard/orders/loading.tsx b/app/dashboard/orders/loading.tsx new file mode 100644 index 0000000..63532d6 --- /dev/null +++ b/app/dashboard/orders/loading.tsx @@ -0,0 +1,15 @@ +import Layout from "@/components/layout/layout"; +import PageLoading from "@/components/dashboard/page-loading"; + +export default function OrdersLoading() { + return ( + + + + ); +} \ No newline at end of file diff --git a/app/dashboard/products/loading.tsx b/app/dashboard/products/loading.tsx new file mode 100644 index 0000000..ee033d9 --- /dev/null +++ b/app/dashboard/products/loading.tsx @@ -0,0 +1,15 @@ +import Layout from "@/components/layout/layout"; +import PageLoading from "@/components/dashboard/page-loading"; + +export default function ProductsLoading() { + return ( + + + + ); +} \ No newline at end of file diff --git a/components/dashboard/page-loading.tsx b/components/dashboard/page-loading.tsx new file mode 100644 index 0000000..35f94b9 --- /dev/null +++ b/components/dashboard/page-loading.tsx @@ -0,0 +1,114 @@ +"use client" + +import { Skeleton } from "@/components/ui/skeleton"; +import { Card } from "@/components/ui/card"; +import { Loader2 } from "lucide-react"; + +interface PageLoadingProps { + title?: string; + subtitle?: string; + itemsCount?: number; + layout?: 'list' | 'grid' | 'table'; +} + +export default function PageLoading({ + title = "Loading data...", + subtitle, + itemsCount = 5, + layout = 'list' +}: PageLoadingProps) { + return ( +
+ {/* Header skeleton */} +
+
+ + {subtitle && } +
+ +
+ + {/* Main content skeleton */} + +
+
+ +

{title}

+ {subtitle &&

{subtitle}

} +
+
+ + {layout === 'list' && ( +
+ {[...Array(itemsCount)].map((_, i) => ( +
+ +
+ + +
+
+ + +
+
+ ))} +
+ )} + + {layout === 'grid' && ( +
+ {[...Array(itemsCount)].map((_, i) => ( + + + + +
+ + +
+
+ ))} +
+ )} + + {layout === 'table' && ( +
+
+
+ + + + +
+
+ +
+ {[...Array(itemsCount)].map((_, i) => ( +
+
+
+ + +
+
+ + +
+
+ +
+
+ + +
+
+
+ ))} +
+
+ )} +
+
+ ); +} \ No newline at end of file diff --git a/lib/api-utils.ts b/lib/api-utils.ts index 4ee689d..6a3ac4c 100644 --- a/lib/api-utils.ts +++ b/lib/api-utils.ts @@ -1,4 +1,5 @@ /** +<<<<<<< Updated upstream * API utilities for consistent request handling */ @@ -33,17 +34,63 @@ export function getServerApiUrl(endpoint: string): string { return apiUrl.endsWith('/') ? `${apiUrl}${cleanEndpoint}` : `${apiUrl}/${cleanEndpoint}`; +======= + * API utilities for client and server-side requests + */ + +/** + * Normalizes the API URL to ensure it uses the proper prefix + * For client-side, ensures all requests go through the Next.js API proxy + */ +export function normalizeApiUrl(url: string): string { + // If URL already starts with http or https, return as is + if (url.startsWith('http://') || url.startsWith('https://')) { + return url; + } + + // If URL already starts with /api, use as is + if (url.startsWith('/api/')) { + return url; + } + + // Otherwise, ensure it has the /api prefix + return `/api${url.startsWith('/') ? '' : '/'}${url}`; +} + +/** + * Get the server API URL for server-side requests + */ +export function getServerApiUrl(endpoint: string): string { + // Get the base API URL from environment + const baseUrl = process.env.SERVER_API_URL || process.env.NEXT_PUBLIC_API_URL || 'https://internal-api.inboxi.ng/api'; + + // Ensure it doesn't have trailing slash + const normalizedBaseUrl = baseUrl.endsWith('/') ? baseUrl.slice(0, -1) : baseUrl; + + // Ensure endpoint has leading slash + const normalizedEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`; + + return `${normalizedBaseUrl}${normalizedEndpoint}`; +>>>>>>> Stashed changes } /** * Get the authentication token from cookies or localStorage +<<<<<<< Updated upstream */ export function getAuthToken(): string | null { if (typeof document === 'undefined') return null; // Guard for SSR +======= + * Only available in client-side code + */ +export function getAuthToken(): string | null { + if (typeof document === 'undefined') return null; +>>>>>>> Stashed changes return document.cookie .split('; ') .find(row => row.startsWith('Authorization=')) +<<<<<<< Updated upstream ?.split('=')[1] || localStorage.getItem('Authorization'); } @@ -72,5 +119,27 @@ export function createApiHeaders(token?: string | null, customHeaders: Record = {}): Headers { + const headers = new Headers({ + 'Content-Type': 'application/json', + ...additionalHeaders + }); + + // Use provided token or try to get it from storage + const authToken = token || getAuthToken(); + + if (authToken) { + headers.append('Authorization', `Bearer ${authToken}`); + } + +>>>>>>> Stashed changes return headers; } \ No newline at end of file diff --git a/lib/client-utils.ts b/lib/client-utils.ts index 59a0676..fccbe20 100644 --- a/lib/client-utils.ts +++ b/lib/client-utils.ts @@ -21,12 +21,6 @@ export async function clientFetch(url: string, options: RequestInit = { if (!res.ok) { const errorData = await res.json().catch(() => ({})); const errorMessage = errorData.message || errorData.error || `Request failed: ${res.status} ${res.statusText}`; - console.error('API Error:', { - status: res.status, - url: fullUrl, - response: errorData, - method: options.method || 'GET' - }); throw new Error(errorMessage); } diff --git a/next.config.mjs b/next.config.mjs index 29ae3a9..e28e8c9 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -24,6 +24,7 @@ const nextConfig = { }, ]; }, +<<<<<<< Updated upstream // Build optimization settings for slower CPUs experimental: { swcMinify: true, @@ -31,6 +32,8 @@ const nextConfig = { logLevel: 'error' } }, +======= +>>>>>>> Stashed changes // Reduce memory usage during builds onDemandEntries: { // Period (in ms) where the server will keep pages in the buffer From f5b5608f9d85a190d951ca531f974b28145aae79 Mon Sep 17 00:00:00 2001 From: NotII <46204250+NotII@users.noreply.github.com> Date: Mon, 24 Mar 2025 13:44:44 +0000 Subject: [PATCH 2/3] erm what the sigma --- lib/api-utils.ts | 18 ++++++++++++++++++ next.config.mjs | 3 +++ 2 files changed, 21 insertions(+) diff --git a/lib/api-utils.ts b/lib/api-utils.ts index 6a3ac4c..24b7327 100644 --- a/lib/api-utils.ts +++ b/lib/api-utils.ts @@ -1,4 +1,5 @@ /** +<<<<<<< Updated upstream <<<<<<< Updated upstream * API utilities for consistent request handling */ @@ -35,6 +36,8 @@ export function getServerApiUrl(endpoint: string): string { ? `${apiUrl}${cleanEndpoint}` : `${apiUrl}/${cleanEndpoint}`; ======= +======= +>>>>>>> Stashed changes * API utilities for client and server-side requests */ @@ -71,25 +74,35 @@ export function getServerApiUrl(endpoint: string): string { const normalizedEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`; return `${normalizedBaseUrl}${normalizedEndpoint}`; +<<<<<<< Updated upstream +>>>>>>> Stashed changes +======= >>>>>>> Stashed changes } /** * Get the authentication token from cookies or localStorage +<<<<<<< Updated upstream <<<<<<< Updated upstream */ export function getAuthToken(): string | null { if (typeof document === 'undefined') return null; // Guard for SSR ======= +======= +>>>>>>> Stashed changes * Only available in client-side code */ export function getAuthToken(): string | null { if (typeof document === 'undefined') return null; +<<<<<<< Updated upstream +>>>>>>> Stashed changes +======= >>>>>>> Stashed changes return document.cookie .split('; ') .find(row => row.startsWith('Authorization=')) +<<<<<<< Updated upstream <<<<<<< Updated upstream ?.split('=')[1] || localStorage.getItem('Authorization'); } @@ -120,6 +133,8 @@ export function createApiHeaders(token?: string | null, customHeaders: Record>>>>>> Stashed changes ?.split('=')[1] || (typeof localStorage !== 'undefined' ? localStorage.getItem('Authorization') : null); } @@ -140,6 +155,9 @@ export function createApiHeaders(token: string | null = null, additionalHeaders: headers.append('Authorization', `Bearer ${authToken}`); } +<<<<<<< Updated upstream +>>>>>>> Stashed changes +======= >>>>>>> Stashed changes return headers; } \ No newline at end of file diff --git a/next.config.mjs b/next.config.mjs index e28e8c9..f31f3ba 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -24,6 +24,7 @@ const nextConfig = { }, ]; }, +<<<<<<< Updated upstream <<<<<<< Updated upstream // Build optimization settings for slower CPUs experimental: { @@ -33,6 +34,8 @@ const nextConfig = { } }, ======= +>>>>>>> Stashed changes +======= >>>>>>> Stashed changes // Reduce memory usage during builds onDemandEntries: { From d3c61ef1de395bcca38213b207d7b37060e12b02 Mon Sep 17 00:00:00 2001 From: NotII <46204250+NotII@users.noreply.github.com> Date: Mon, 24 Mar 2025 13:46:53 +0000 Subject: [PATCH 3/3] erm what the sigma --- lib/api-utils.ts | 92 ------------------------------------------------ next.config.mjs | 12 ++----- 2 files changed, 3 insertions(+), 101 deletions(-) diff --git a/lib/api-utils.ts b/lib/api-utils.ts index 24b7327..6801bcf 100644 --- a/lib/api-utils.ts +++ b/lib/api-utils.ts @@ -1,43 +1,4 @@ /** -<<<<<<< Updated upstream -<<<<<<< Updated upstream - * API utilities for consistent request handling - */ - -/** - * Normalizes a URL to ensure it passes through the Next.js API proxy - * This ensures all client-side requests go through the Next.js rewrites. - * - * @param url The endpoint URL to normalize - * @returns A normalized URL with proper /api prefix - */ -export function normalizeApiUrl(url: string): string { - if (url.startsWith('/api/')) { - return url; // Already has /api/ prefix - } else { - // Add /api prefix, handling any leading slashes - const cleanUrl = url.startsWith('/') ? url : `/${url}`; - return `/api${cleanUrl}`; - } -} - -/** - * Constructs a server-side API URL for backend requests - * Used in Server Components and API routes to directly access the backend API - * - * @param endpoint The API endpoint path - * @returns A complete URL to the backend API - */ -export function getServerApiUrl(endpoint: string): string { - const apiUrl = process.env.SERVER_API_URL || 'https://internal-api.inboxi.ng/api'; - const cleanEndpoint = endpoint.startsWith('/') ? endpoint.substring(1) : endpoint; - - return apiUrl.endsWith('/') - ? `${apiUrl}${cleanEndpoint}` - : `${apiUrl}/${cleanEndpoint}`; -======= -======= ->>>>>>> Stashed changes * API utilities for client and server-side requests */ @@ -74,67 +35,18 @@ export function getServerApiUrl(endpoint: string): string { const normalizedEndpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`; return `${normalizedBaseUrl}${normalizedEndpoint}`; -<<<<<<< Updated upstream ->>>>>>> Stashed changes -======= ->>>>>>> Stashed changes } /** * Get the authentication token from cookies or localStorage -<<<<<<< Updated upstream -<<<<<<< Updated upstream - */ -export function getAuthToken(): string | null { - if (typeof document === 'undefined') return null; // Guard for SSR -======= -======= ->>>>>>> Stashed changes * Only available in client-side code */ export function getAuthToken(): string | null { if (typeof document === 'undefined') return null; -<<<<<<< Updated upstream ->>>>>>> Stashed changes -======= ->>>>>>> Stashed changes return document.cookie .split('; ') .find(row => row.startsWith('Authorization=')) -<<<<<<< Updated upstream -<<<<<<< Updated upstream - ?.split('=')[1] || localStorage.getItem('Authorization'); -} - -/** - * Check if the user is logged in - */ -export function isAuthenticated(): boolean { - return !!getAuthToken(); -} - -/** - * Creates standard API request headers with authentication - * - * @param token Optional auth token (fetched automatically if not provided) - * @param customHeaders Additional headers to include - * @returns Headers object ready for fetch requests - */ -export function createApiHeaders(token?: string | null, customHeaders: Record = {}): Headers { - const headers = new Headers({ - 'Content-Type': 'application/json', - ...customHeaders - }); - - const authToken = token || getAuthToken(); - if (authToken) { - headers.set('Authorization', `Bearer ${authToken}`); - } - -======= -======= ->>>>>>> Stashed changes ?.split('=')[1] || (typeof localStorage !== 'undefined' ? localStorage.getItem('Authorization') : null); } @@ -155,9 +67,5 @@ export function createApiHeaders(token: string | null = null, additionalHeaders: headers.append('Authorization', `Bearer ${authToken}`); } -<<<<<<< Updated upstream ->>>>>>> Stashed changes -======= ->>>>>>> Stashed changes return headers; } \ No newline at end of file diff --git a/next.config.mjs b/next.config.mjs index f31f3ba..17658c4 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -23,22 +23,16 @@ const nextConfig = { destination: 'https://internal-api.inboxi.ng/api/:path*', }, ]; - }, -<<<<<<< Updated upstream -<<<<<<< Updated upstream + } , // Build optimization settings for slower CPUs experimental: { swcMinify: true, turbotrace: { logLevel: 'error' } - }, -======= ->>>>>>> Stashed changes -======= ->>>>>>> Stashed changes + }, // Reduce memory usage during builds - onDemandEntries: { + onDemandEntries: { // Period (in ms) where the server will keep pages in the buffer maxInactiveAge: 15 * 1000, // Number of pages that should be kept simultaneously without being disposed