diff --git a/.gitignore b/.gitignore
index 70ea34a..74d98c9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,3 +42,5 @@ next-env.d.ts
# logs
logs
*.log
+
+app/(test)
\ No newline at end of file
diff --git a/app/(main)/buckets/layout.tsx b/app/(main)/buckets/layout.tsx
index c9ba126..27e9df6 100644
--- a/app/(main)/buckets/layout.tsx
+++ b/app/(main)/buckets/layout.tsx
@@ -1,51 +1,12 @@
-import { Metadata, Viewport } from "next";
-import dynamic from "next/dynamic";
-import DashboardNavSkeleton from "@/components/custom-ui/skeleton/DashboardNavSkeleton";
+import { Viewport } from "next";
import BackgroundShape from "@/components/custom-ui/home/BackgroundShape";
+import DashboardNav from "@/components/layout/DashboardNav";
export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
};
-export const metadata: Metadata = {
- title: "Buckets | Nebula S3",
- description:
- "Manage and organize your S3 buckets with Nebula S3. Create, monitor, and maintain your cloud storage buckets efficiently.",
- keywords: [
- "S3 Buckets",
- "Cloud Storage",
- "Bucket Management",
- "Storage Organization",
- "Cloud Infrastructure",
- "Data Storage",
- "Bucket Monitoring",
- "Storage Analytics",
- ],
- authors: [{ name: "Nebula S3 Team" }],
- robots: "index, follow",
- openGraph: {
- type: "website",
- locale: "en_US",
- url: "https://nebula-s3.com/buckets",
- title: "NEBULA S3 BUCKETS",
- description:
- "Manage and organize your S3 buckets with Nebula S3. Create, monitor, and maintain your cloud storage buckets efficiently.",
- siteName: "NEBULA S3",
- },
- twitter: {
- card: "summary_large_image",
- title: "NEBULA S3 BUCKETS",
- description:
- "Manage and organize your S3 buckets with Nebula S3. Create, monitor, and maintain your cloud storage buckets efficiently.",
- creator: "@nebulas3",
- },
-};
-
-const DashboardNav = dynamic(() => import("@/components/layout/DashboardNav"), {
- loading: () => ,
-});
-
export default function BucketsLayout({
children,
}: {
diff --git a/app/(main)/buckets/page.tsx b/app/(main)/buckets/page.tsx
index e7fdfb1..9842605 100644
--- a/app/(main)/buckets/page.tsx
+++ b/app/(main)/buckets/page.tsx
@@ -1,102 +1,45 @@
-"use client";
-
-import { useState, Suspense, useEffect } from "react";
-import dynamic from "next/dynamic";
-import BucketHeaderSkeleton from "@/components/custom-ui/skeleton/bucket-header-skeleton";
-import BucketFiltersSkeleton from "@/components/custom-ui/skeleton/bucket-filters-skeleton";
-import BucketGridSkeleton from "@/components/custom-ui/skeleton/bucket-grid-skeleton";
-
-// Dynamic imports with skeleton loading states
-const BucketHeader = dynamic(
- () => import("@/components/custom-ui/bucket-component/bucket-header"),
- {
- loading: () => ,
- ssr: false,
- }
-);
-
-const BucketFilters = dynamic(
- () => import("@/components/custom-ui/bucket-component/bucket-filters"),
- {
- loading: () => ,
- ssr: false,
- }
-);
-
-const BucketGrid = dynamic(
- () => import("@/components/custom-ui/bucket-component/bucket-grid"),
- {
- loading: () => ,
- ssr: false,
- }
-);
-
-// Static mock data
-import { mockBuckets } from "@/static/buckets";
-
-export default function BucketsPage() {
- const [mounted, setMounted] = useState(false);
- const [searchQuery, setSearchQuery] = useState("");
- const [activeFilter, setActiveFilter] = useState<"size" | "date" | null>(
- null
- );
-
- useEffect(() => {
- setMounted(true);
- }, []);
-
- const filteredBuckets = mockBuckets
- .filter((bucket) =>
- bucket.bucketName.toLowerCase().includes(searchQuery.toLowerCase())
- )
- .sort((a, b) => {
- if (activeFilter === "size") {
- const sizeA = parseFloat(a.size);
- const sizeB = parseFloat(b.size);
- return sizeB - sizeA;
- } else if (activeFilter === "date") {
- return b.createdOn.getTime() - a.createdOn.getTime();
- }
- return 0;
- });
-
- const handleBucketDelete = (bucketId: string) => {
- // Implement delete functionality
- console.log("Delete bucket:", bucketId);
- };
-
- if (!mounted) {
- return (
-
- );
- }
-
- return (
-
-
- }>
-
-
-
- }>
-
-
-
- }>
-
-
-
-
- );
-}
+import { Metadata } from "next";
+import BucketManagement from "@/components/custom-ui/bucket-component/bucket-management";
+import { ErrorBoundary } from "@/components/ErrorBoundary";
+
+export const metadata: Metadata = {
+ title: "Buckets | Nebula S3",
+ description:
+ "Manage and organize your S3 buckets with Nebula S3. Create, monitor, and maintain your cloud storage buckets efficiently.",
+ keywords: [
+ "S3 Buckets",
+ "Cloud Storage",
+ "Bucket Management",
+ "Storage Organization",
+ "Cloud Infrastructure",
+ "Data Storage",
+ "Bucket Monitoring",
+ "Storage Analytics",
+ ],
+ authors: [{ name: "Nebula S3 Team" }],
+ robots: "index, follow",
+ openGraph: {
+ type: "website",
+ locale: "en_US",
+ url: "https://nebula-s3.com/buckets",
+ title: "NEBULA S3 BUCKETS",
+ description:
+ "Manage and organize your S3 buckets with Nebula S3. Create, monitor, and maintain your cloud storage buckets efficiently.",
+ siteName: "NEBULA S3",
+ },
+ twitter: {
+ card: "summary_large_image",
+ title: "NEBULA S3 BUCKETS",
+ description:
+ "Manage and organize your S3 buckets with Nebula S3. Create, monitor, and maintain your cloud storage buckets efficiently.",
+ creator: "@nebulas3",
+ },
+};
+
+export default function BucketsPage() {
+ return (
+
+
+
+ );
+}
diff --git a/app/(main)/dashboard/layout.tsx b/app/(main)/dashboard/layout.tsx
index 9b61437..0b324ed 100755
--- a/app/(main)/dashboard/layout.tsx
+++ b/app/(main)/dashboard/layout.tsx
@@ -1,46 +1,13 @@
-import { Metadata, Viewport } from "next";
-import dynamic from "next/dynamic";
-import DashboardNavSkeleton from "@/components/custom-ui/skeleton/DashboardNavSkeleton";
+import { Viewport } from "next";
import BackgroundShape from "@/components/custom-ui/home/BackgroundShape";
import Footer from "@/components/layout/Footer";
+import DashboardNav from "@/components/layout/DashboardNav";
export const viewport: Viewport = {
width: "device-width",
initialScale: 1,
};
-export const metadata: Metadata = {
- title: "DASHBOARD | NEBULA S3",
- description: "Manage your S3 buckets and objects with Nebula S3 dashboard",
- keywords: [
- "S3",
- "Storage",
- "Dashboard",
- "Cloud Storage",
- "Bucket Management",
- ],
- authors: [{ name: "Nebula S3 Team" }],
- robots: "index, follow",
- openGraph: {
- type: "website",
- locale: "en_US",
- url: "https://nebula-s3.com/dashboard",
- title: "NEBULA S3 DASHBOARD",
- description: "Manage your S3 buckets and objects with Nebula S3 dashboard",
- siteName: "NEBULA S3",
- },
- twitter: {
- card: "summary_large_image",
- title: "NEBULA S3 DASHBOARD",
- description: "Manage your S3 buckets and objects with Nebula S3 dashboard",
- creator: "@nebulas3",
- },
-};
-
-const DashboardNav = dynamic(() => import("@/components/layout/DashboardNav"), {
- loading: () => ,
-});
-
export default function DashboardLayout({
children,
}: {
diff --git a/app/(main)/dashboard/page.tsx b/app/(main)/dashboard/page.tsx
index 029f2a4..f84adc0 100755
--- a/app/(main)/dashboard/page.tsx
+++ b/app/(main)/dashboard/page.tsx
@@ -1,15 +1,42 @@
+import { Metadata } from "next";
import { Suspense } from "react";
import DashboardSkeleton from "@/components/custom-ui/skeleton/DashboardSkeleton";
import DashboardContent from "@/components/custom-ui/dashboard/DashboardContent";
+export const metadata: Metadata = {
+ title: "Dashboard | Nebula S3",
+ description: "Manage your S3 buckets and objects with Nebula S3 dashboard",
+ keywords: [
+ "S3",
+ "Storage",
+ "Dashboard",
+ "Cloud Storage",
+ "Bucket Management",
+ ],
+ authors: [{ name: "Nebula S3 Team" }],
+ robots: "index, follow",
+ openGraph: {
+ type: "website",
+ locale: "en_US",
+ url: "https://nebula-s3.com/dashboard",
+ title: "NEBULA S3 DASHBOARD",
+ description: "Manage your S3 buckets and objects with Nebula S3 dashboard",
+ siteName: "NEBULA S3",
+ },
+ twitter: {
+ card: "summary_large_image",
+ title: "NEBULA S3 DASHBOARD",
+ description: "Manage your S3 buckets and objects with Nebula S3 dashboard",
+ creator: "@nebulas3",
+ },
+};
+
export default function DashboardPage() {
return (
- <>
-
- }>
-
-
-
- >
+
+ }>
+
+
+
);
}
diff --git a/app/(pages)/about/layout.tsx b/app/(pages)/about/layout.tsx
deleted file mode 100755
index 4a8b41a..0000000
--- a/app/(pages)/about/layout.tsx
+++ /dev/null
@@ -1,48 +0,0 @@
-import { Metadata } from "next";
-import React from "react";
-import BackgroundShape from "@/components/custom-ui/home/BackgroundShape";
-
-interface AboutLayoutProps {
- readonly children: React.ReactNode;
-}
-
-export const metadata: Metadata = {
- title: "About NebulaS3 - Secure & Advanced AWS S3 Management",
- description:
- "Learn more about NebulaS3, an advanced SaaS platform for secure Amazon S3 management using AWS STS authentication, role-based access, and real-time bucket operations.",
- keywords: [
- "NebulaS3",
- "AWS S3 Management",
- "Amazon S3 SaaS",
- "AWS STS Authentication",
- "Secure S3 Access",
- "S3 Bucket Management",
- "Cloud Storage Security",
- "Role-Based Access Control",
- "AWS SDK Integration",
- ],
- openGraph: {
- title: "NebulaS3 - Advanced Amazon S3 Management Platform",
- description:
- "NebulaS3 offers secure and efficient AWS S3 management with role-based authentication, temporary credentials, and real-time bucket operations.",
- url: "https://nebulas3.vercel.app/about",
- type: "website",
- images: [
- {
- url: "https://miro.medium.com/v2/resize:fit:800/1*kiR4fwOJnlHP2bxnYrm1Xg.jpeg",
- width: 1200,
- height: 630,
- alt: "NebulaS3 - AWS S3 Management",
- },
- ],
- },
-};
-
-export default function AboutLayout({ children }: AboutLayoutProps) {
- return (
-
- );
-}
diff --git a/app/(pages)/about/page.tsx b/app/(pages)/about/page.tsx
index b5eb8d2..01e716e 100755
--- a/app/(pages)/about/page.tsx
+++ b/app/(pages)/about/page.tsx
@@ -1,26 +1,26 @@
-"use client";
+import { Metadata } from "next";
+import BackNavigation from "@/components/custom-ui/navigation/back-navigation";
-import React from "react";
-import { useRouter } from "next/navigation";
-import CustomButton from "@/components/ui/custom-button";
-import { ArrowLeft } from "lucide-react";
-
-export default function Page() {
- const router = useRouter();
-
- const handleBack = () => {
- router.back();
- };
+export const metadata: Metadata = {
+ title: "About | Nebula S3",
+ description: "Learn about NebulaS3 - our advanced storage solution built on the foundation of S3 with enhanced capabilities.",
+ keywords: [
+ "About",
+ "NebulaS3",
+ "S3 Storage",
+ "Cloud Storage",
+ "Storage Solution",
+ "Features",
+ ],
+ authors: [{ name: "Nebula S3 Team" }],
+ robots: "index, follow",
+};
+export default function AboutPage() {
return (
- }
- />
+
diff --git a/app/(pages)/contact/layout.tsx b/app/(pages)/contact/layout.tsx
deleted file mode 100755
index ec76a71..0000000
--- a/app/(pages)/contact/layout.tsx
+++ /dev/null
@@ -1,47 +0,0 @@
-import { Metadata } from "next";
-import React from "react";
-import BackgroundShape from "@/components/custom-ui/home/BackgroundShape";
-
-interface ContactLayoutProps {
- children: React.ReactNode;
-}
-
-export const metadata: Metadata = {
- title: "Contact NebulaS3 - Get in Touch for Support & Inquiries",
- description:
- "Reach out to NebulaS3 for support, partnership opportunities, or general inquiries. Our team is ready to assist you with AWS S3 management solutions.",
- keywords: [
- "Contact NebulaS3",
- "NebulaS3 Support",
- "AWS S3 Assistance",
- "Cloud Storage Help",
- "S3 Management Queries",
- "Amazon S3 SaaS Support",
- "Customer Support NebulaS3",
- "Cloud Storage Solutions",
- ],
- openGraph: {
- title: "Contact NebulaS3 - AWS S3 Support & Queries",
- description:
- "Have questions or need support for NebulaS3? Contact our team for assistance with AWS S3 management and cloud storage solutions.",
- url: "https://nebulas3.vercel.app/contact",
- type: "website",
- images: [
- {
- url: "https://yourdomain.com/images/contact-og.jpg",
- width: 1200,
- height: 630,
- alt: "Contact NebulaS3 - AWS S3 Management Support",
- },
- ],
- },
-};
-
-export default function ContactLayout({ children }: ContactLayoutProps) {
- return (
-
- );
-}
diff --git a/app/(pages)/contact/page.tsx b/app/(pages)/contact/page.tsx
index e432626..a218abc 100755
--- a/app/(pages)/contact/page.tsx
+++ b/app/(pages)/contact/page.tsx
@@ -1,22 +1,27 @@
-"use client";
-
-import React from "react";
-import { useRouter } from "next/navigation";
+import { Metadata } from "next";
import Link from "next/link";
-import CustomButton from "@/components/ui/custom-button";
-import { ArrowLeft } from "lucide-react";
-
-export default function Page() {
- const router = useRouter();
+import BackNavigation from "@/components/custom-ui/navigation/back-navigation";
- const handleBack = () => {
- router.back();
- };
+export const metadata: Metadata = {
+ title: "Contact | Nebula S3",
+ description: "Get in touch with the Nebula S3 team. We're here to help with your cloud storage needs.",
+ keywords: [
+ "Contact",
+ "Support",
+ "Help",
+ "Nebula S3",
+ "Customer Service",
+ "Get in Touch",
+ ],
+ authors: [{ name: "Nebula S3 Team" }],
+ robots: "index, follow",
+};
+export default function ContactPage() {
return (
- } />
+
@@ -34,7 +39,7 @@ export default function Page() {
Email Support
- mail.vikas99blr@gmail.com
+ vikas99blr@gmail.com
diff --git a/app/api/auth/login/route.ts b/app/api/auth/login/route.ts
deleted file mode 100644
index e9862a5..0000000
--- a/app/api/auth/login/route.ts
+++ /dev/null
@@ -1,21 +0,0 @@
-import { NextResponse } from "next/server";
-import { login, LoginCredentials } from "@/services/authService";
-
-// This keeps the API base URL away from client-side code
-export async function POST(request: Request) {
- try {
- const body = await request.json();
- const credentials: LoginCredentials = body;
-
- // Use the authService to make the actual API call
- const response = await login(credentials);
-
- return NextResponse.json(response);
- } catch (error) {
- console.error("API route error:", error);
- return NextResponse.json(
- { status: "ERROR", message: "Failed to authenticate" },
- { status: 500 }
- );
- }
-}
diff --git a/app/api/health/route.ts b/app/api/health/route.ts
deleted file mode 100644
index 7d190d5..0000000
--- a/app/api/health/route.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { NextResponse } from "next/server";
-import { checkHealth } from "@/services/healthService";
-
-// This keeps the API base URL away from client-side code
-export async function GET() {
- try {
- // Use the healthService to make the actual API call
- const response = await checkHealth();
-
- return NextResponse.json(response);
- } catch (error) {
- console.error("Health check API route error:", error);
- return NextResponse.json(
- { status: "FAILED", message: "Service health check failed" },
- { status: 500 }
- );
- }
-}
diff --git a/app/auth/login/layout.tsx b/app/auth/login/layout.tsx
deleted file mode 100755
index 9c1b280..0000000
--- a/app/auth/login/layout.tsx
+++ /dev/null
@@ -1,20 +0,0 @@
-import { Metadata } from "next";
-import React from "react";
-
-interface LoginLayoutProps {
- readonly children: React.ReactNode;
-}
-
-// Streamlined metadata to reduce bundle size
-export const metadata: Metadata = {
- title: "Login to NebulaS3",
- description: "Access your NebulaS3 account securely.",
-};
-
-export default function LoginLayout({ children }: LoginLayoutProps) {
- return (
-
- {children}
-
- );
-}
diff --git a/app/auth/login/page.tsx b/app/auth/login/page.tsx
index fd275b5..403a6b7 100755
--- a/app/auth/login/page.tsx
+++ b/app/auth/login/page.tsx
@@ -1,9 +1,21 @@
-"use client";
-
-import { ArrowLeft } from "lucide-react";
-import dynamic from "next/dynamic";
+import { Metadata } from "next";
import Link from "next/link";
-import BackButton from "@/components/ui/custom-button";
+import LoginPageClient from "@/components/custom-ui/auth/login-page-client";
+
+export const metadata: Metadata = {
+ title: "Login | Nebula S3",
+ description: "Sign in to your Nebula S3 account to manage your AWS S3 buckets with advanced security and features.",
+ keywords: [
+ "Login",
+ "Sign In",
+ "Authentication",
+ "Nebula S3",
+ "AWS S3 Login",
+ "Secure Access",
+ ],
+ authors: [{ name: "Nebula S3 Team" }],
+ robots: "index, follow",
+};
// Optimize logo component to reduce imports
const Logo = () => (
@@ -28,22 +40,6 @@ const Logo = () => (
);
-// Load components with aggressive code splitting and skeleton fallbacks
-const LoginForm = dynamic(
- () => import("@/components/custom-ui/auth/login-form"),
- {
- loading: () => ,
- ssr: false,
- }
-);
-
-const LoginSkeleton = dynamic(
- () => import("@/components/custom-ui/skeleton/LoginSkeleton"),
- {
- ssr: false,
- }
-);
-
export default function LoginPage() {
return (
@@ -57,19 +53,7 @@ export default function LoginPage() {
NebulaS3
-
-
window.history.back()}
- icon={}
- />
-
-
+
{/* Replaced Image with CSS background for optimization */}
diff --git a/app/globals.css b/app/globals.css
index 3ce8907..d01e19d 100644
--- a/app/globals.css
+++ b/app/globals.css
@@ -41,103 +41,128 @@
:root {
--radius: 0.625rem;
- --background: #ffffff;
- --foreground: #000000;
+ /* AWS Cloudscape Design System - Light Mode */
+ --background: #fafbfc;
+ --foreground: #16191f;
- --card: rgba(255, 255, 255, 0.8);
- --card-foreground: #000000;
+ --card: #ffffff;
+ --card-foreground: #16191f;
- /* Glassmorphism variables */
- --glass-background: rgba(255, 255, 255, 0.25);
- --glass-border: rgba(255, 255, 255, 0.18);
- --glass-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.15);
- --glass-blur: 10px;
+ /* Enhanced glassmorphism with AWS aesthetic */
+ --glass-background: rgba(255, 255, 255, 0.92);
+ --glass-border: rgba(22, 25, 31, 0.06);
+ --glass-shadow: 0 2px 16px 0 rgba(22, 25, 31, 0.04);
+ --glass-blur: 16px;
--popover: #ffffff;
- --popover-foreground: #000000;
+ --popover-foreground: #16191f;
- --primary: #000000;
+ /* AWS Primary Blue (updated from Orange to match modern AWS) */
+ --primary: #0972d3;
--primary-foreground: #ffffff;
- --secondary: #f3f3f3;
- --secondary-foreground: #000000;
+ /* AWS Neutral grays */
+ --secondary: #f2f3f3;
+ --secondary-foreground: #16191f;
- --muted: #f6f6f6;
- --muted-foreground: #666666;
+ /* AWS Surface colors */
+ --muted: #f9f9f9;
+ --muted-foreground: #5f6b7a;
- --accent: #e0e0e0;
- --accent-foreground: #000000;
+ /* AWS Interactive blue accents */
+ --accent: #e9f3ff;
+ --accent-foreground: #0972d3;
- --destructive: #ff4d4f;
+ /* AWS Status red */
+ --destructive: #d91515;
- --border: #e0e0e0;
- --input: #e0e0e0;
- --ring: #cccccc;
+ /* AWS Modern borders */
+ --border: #e9ebed;
+ --input: #e9ebed;
+ --ring: #0972d3;
- --chart-1: #ff6b6b;
- --chart-2: #4dabf7;
- --chart-3: #9775fa;
- --chart-4: #51cf66;
- --chart-5: #fcc419;
+ /* AWS Status & Chart colors (Cloudscape tokens) */
+ --chart-1: #0972d3; /* Primary Blue */
+ --chart-2: #ff9900; /* AWS Orange */
+ --chart-3: #037f0c; /* Success Green */
+ --chart-4: #d91515; /* Error Red */
+ --chart-5: #8c4fff; /* Purple */
- --sidebar: #ffffff;
- --sidebar-foreground: #000000;
- --sidebar-primary: #000000;
+ /* Modern sidebar with AWS branding */
+ --sidebar: #16191f;
+ --sidebar-foreground: #ffffff;
+ --sidebar-primary: #0972d3;
--sidebar-primary-foreground: #ffffff;
- --sidebar-accent: #f3f3f3;
- --sidebar-accent-foreground: #000000;
- --sidebar-border: #e0e0e0;
- --sidebar-ring: #cccccc;
-}
-
-.dark {
- --background: #000000;
- --foreground: #ffffff;
-
- --card: rgba(26, 26, 26, 0.8);
- --card-foreground: #ffffff;
-
- /* Glassmorphism variables for dark mode */
- --glass-background: rgba(26, 26, 26, 0.25);
- --glass-border: rgba(255, 255, 255, 0.08);
- --glass-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.3);
- --glass-blur: 10px;
-
- --popover: #1a1a1a;
- --popover-foreground: #ffffff;
-
- --primary: #ffffff;
- --primary-foreground: #000000;
-
- --secondary: #333333;
- --secondary-foreground: #ffffff;
-
- --muted: #2a2a2a;
- --muted-foreground: #aaaaaa;
-
- --accent: #3a3a3a;
- --accent-foreground: #ffffff;
-
- --destructive: #ff6b6b;
+ --sidebar-accent: #414d5c;
+ --sidebar-accent-foreground: #ffffff;
+ --sidebar-border: #414d5c;
+ --sidebar-ring: #0972d3;
- --border: #333333;
- --input: #333333;
- --ring: #666666;
+ /* Cross-browser scrollbar hiding */
+ scrollbar-width: none; /* Firefox */
+ -ms-overflow-style: none; /* IE and Edge */
+}
- --chart-1: #f03e3e;
- --chart-2: #228be6;
- --chart-3: #845ef7;
- --chart-4: #40c057;
- --chart-5: #fab005;
+/* Webkit browsers scrollbar hiding */
+:root::-webkit-scrollbar {
+ display: none; /* Chrome, Safari, Opera */
+}
- --sidebar: #1a1a1a;
- --sidebar-foreground: #ffffff;
- --sidebar-primary: #ffffff;
- --sidebar-primary-foreground: #000000;
- --sidebar-accent: #333333;
- --sidebar-accent-foreground: #ffffff;
- --sidebar-border: #333333;
- --sidebar-ring: #666666;
+.dark {
+ --background: #0d1117;
+ --foreground: #f0f6fc;
+
+ --card: rgba(22, 27, 34, 0.9);
+ --card-foreground: #f0f6fc;
+
+ /* Enhanced dark mode glassmorphism */
+ --glass-background: rgba(22, 27, 34, 0.3);
+ --glass-border: rgba(240, 246, 252, 0.06);
+ --glass-shadow: 0 4px 24px 0 rgba(0, 0, 0, 0.3);
+ --glass-blur: 16px;
+
+ --popover: #161b22;
+ --popover-foreground: #f0f6fc;
+
+ /* Bright primary for dark mode */
+ --primary: #2ea4f0;
+ --primary-foreground: #0d1117;
+
+ /* Dark mode surfaces */
+ --secondary: #21262d;
+ --secondary-foreground: #f0f6fc;
+
+ --muted: #161b22;
+ --muted-foreground: #8b949e;
+
+ /* Dark mode accent */
+ --accent: #1c2c41;
+ --accent-foreground: #2ea4f0;
+
+ /* Dark mode destructive */
+ --destructive: #f85149;
+
+ /* Dark mode borders */
+ --border: #30363d;
+ --input: #21262d;
+ --ring: #2ea4f0;
+
+ /* Dark mode chart colors */
+ --chart-1: #2ea4f0; /* Bright Blue */
+ --chart-2: #ffa657; /* Orange */
+ --chart-3: #3fb950; /* Green */
+ --chart-4: #f85149; /* Red */
+ --chart-5: #a66efc; /* Purple */
+
+ /* Dark sidebar */
+ --sidebar: #0d1117;
+ --sidebar-foreground: #f0f6fc;
+ --sidebar-primary: #2ea4f0;
+ --sidebar-primary-foreground: #0d1117;
+ --sidebar-accent: #21262d;
+ --sidebar-accent-foreground: #f0f6fc;
+ --sidebar-border: #30363d;
+ --sidebar-ring: #2ea4f0;
}
@layer base {
@@ -163,7 +188,7 @@
@apply bg-sidebar-accent text-sidebar-accent-foreground;
}
.glass-nav {
- @apply bg-white/40 dark:bg-black/30 backdrop-blur-2xl backdrop-saturate-150 border-b border-white/20 dark:border-white/5 shadow-sm relative;
+ @apply bg-white/96 dark:bg-gray-900/40 backdrop-blur-2xl backdrop-saturate-150 border-b border-gray-200/40 dark:border-white/5 shadow-sm relative;
}
.glass-nav::before {
content: "";
@@ -171,39 +196,39 @@
inset: 0;
background: radial-gradient(
ellipse at top right,
- rgba(255, 255, 255, 0.15),
+ rgba(9, 114, 211, 0.06),
transparent 70%
);
pointer-events: none;
}
.glass-text {
- @apply text-black/90 dark:text-white/90;
+ @apply text-gray-900 dark:text-gray-100;
}
.glass-hover {
- @apply hover:bg-white/30 dark:hover:bg-white/10 hover:text-black dark:hover:text-white transition-all duration-300;
+ @apply hover:bg-blue-50/70 dark:hover:bg-blue-950/20 hover:text-gray-900 dark:hover:text-white transition-all duration-300;
}
.glass-morphism {
- @apply bg-white/40 dark:bg-black/30 backdrop-blur-xl backdrop-saturate-200 border border-white/30 dark:border-white/10 relative overflow-hidden;
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1), 0 0 15px rgba(0, 0, 0, 0.05),
- inset 0 1px 1px rgba(255, 255, 255, 0.15);
+ @apply bg-white/94 dark:bg-gray-900/30 backdrop-blur-xl backdrop-saturate-200 border border-gray-200/30 dark:border-white/8 relative overflow-hidden;
+ box-shadow: 0 2px 16px rgba(22, 25, 31, 0.04),
+ 0 0 8px rgba(22, 25, 31, 0.02), inset 0 1px 1px rgba(255, 255, 255, 0.95);
}
.glass-card {
- @apply bg-white/30 dark:bg-white/5 backdrop-blur-xl backdrop-saturate-200 border border-white/30 dark:border-white/10 shadow-md transition-all duration-300;
+ @apply bg-white/96 dark:bg-gray-900/20 backdrop-blur-xl backdrop-saturate-200 border border-gray-200/40 dark:border-white/8 shadow-sm transition-all duration-300;
}
.glass-card-title {
- @apply text-black/80 dark:text-white/90 font-medium;
+ @apply text-gray-900 dark:text-gray-100 font-medium;
}
.glass-card-description {
- @apply text-black/60 dark:text-white/60 text-sm;
+ @apply text-gray-600 dark:text-gray-400 text-sm;
}
.glass-card-content {
- @apply text-black/70 dark:text-white/80;
+ @apply text-gray-700 dark:text-gray-300;
}
.glass-card-hover {
- @apply hover:shadow-md hover:shadow-purple-300/30 dark:hover:shadow-purple-800/20 hover:bg-white/40 dark:hover:bg-white/10 transition-all duration-300;
+ @apply hover:shadow-lg hover:shadow-blue-100/40 dark:hover:shadow-blue-900/20 hover:bg-white/98 dark:hover:bg-gray-900/30 hover:border-blue-200/50 dark:hover:border-blue-800/30 transition-all duration-300;
}
.gradient-text {
- @apply bg-gradient-to-r from-white via-gray-200 to-gray-400 dark:from-white dark:via-gray-700 dark:to-gray-500 bg-clip-text text-transparent font-bold;
+ @apply bg-gradient-to-r from-gray-900 via-blue-600 to-blue-500 dark:from-gray-100 dark:via-blue-400 dark:to-blue-300 bg-clip-text text-transparent font-bold;
}
@keyframes card-title-underline {
0% {
@@ -228,16 +253,16 @@
}
.glass-button {
- @apply bg-white/40 dark:bg-white/10 text-black dark:text-white backdrop-blur-md backdrop-saturate-150 border border-white/30 dark:border-white/10 shadow-md hover:shadow-lg hover:bg-white/60 dark:hover:bg-white/20 transition-all duration-300;
+ @apply bg-white/94 dark:bg-gray-900/20 text-gray-900 dark:text-gray-100 backdrop-blur-md backdrop-saturate-150 border border-gray-200/50 dark:border-white/10 shadow-sm hover:shadow-md hover:bg-white/98 hover:border-blue-200/60 dark:hover:bg-gray-900/30 dark:hover:border-blue-700/40 transition-all duration-300;
}
.glass-badge {
- @apply bg-white/30 dark:bg-black/30 backdrop-blur-sm px-2.5 py-1 text-xs font-medium ring-1 ring-inset transition-all duration-200;
+ @apply bg-white/94 dark:bg-gray-900/30 backdrop-blur-sm px-2.5 py-1 text-xs font-medium ring-1 ring-inset ring-gray-200/50 dark:ring-white/10 transition-all duration-200;
}
.glass-sidenav {
- @apply bg-white/80 dark:bg-black/85 backdrop-blur-3xl backdrop-saturate-200 border-l border-white/20 dark:border-white/10 shadow-xl;
+ @apply bg-white/96 dark:bg-gray-900/85 backdrop-blur-3xl backdrop-saturate-200 border-l border-gray-200/30 dark:border-white/8 shadow-xl;
}
.glass-active {
- @apply bg-white/50 dark:bg-white/10 shadow-sm shadow-purple-300/20 dark:shadow-purple-800/10;
+ @apply bg-blue-50/70 dark:bg-blue-950/20 shadow-sm shadow-blue-100/30 dark:shadow-blue-900/10 border-blue-200/50 dark:border-blue-800/30;
}
/* Glass card styles now defined directly in the component with Tailwind */
}
@@ -283,10 +308,206 @@
);
}
-:root {
- scrollbar-width: none; /* Firefox */
- -ms-overflow-style: none; /* IE and Edge */
+/* Dialog Backdrop Blur */
+[data-radix-dialog-overlay] {
+ -webkit-backdrop-filter: blur(8px) saturate(180%);
+ backdrop-filter: blur(8px) saturate(180%);
+ background: rgba(0, 0, 0, 0.2) !important;
}
-:root::-webkit-scrollbar {
- display: none; /* Chrome, Safari, Opera */
+
+/* Dark mode dialog backdrop */
+.dark [data-radix-dialog-overlay] {
+ -webkit-backdrop-filter: blur(8px) saturate(180%);
+ backdrop-filter: blur(8px) saturate(180%);
+ background: rgba(0, 0, 0, 0.4) !important;
+}
+
+/* Custom Scrollbar Styles for Dashboard Content */
+.custom-scrollbar {
+ scrollbar-width: thin;
+ scrollbar-color: transparent transparent;
+ transition: scrollbar-color 0.3s ease;
+}
+
+.custom-scrollbar:hover {
+ scrollbar-color: rgba(255, 255, 255, 0.2) rgba(255, 255, 255, 0.05);
+}
+
+.custom-scrollbar::-webkit-scrollbar {
+ width: 8px;
+ height: 8px;
+}
+
+.custom-scrollbar::-webkit-scrollbar-track {
+ background: transparent;
+ border-radius: 4px;
+}
+
+.custom-scrollbar::-webkit-scrollbar-thumb {
+ background: transparent;
+ border-radius: 4px;
+ border: 1px solid transparent;
+ transition: all 0.3s ease;
+ opacity: 0;
+}
+
+/* Glass morphism scrollbar on hover/scroll */
+.custom-scrollbar:hover::-webkit-scrollbar-thumb,
+.custom-scrollbar:active::-webkit-scrollbar-thumb {
+ background: rgba(255, 255, 255, 0.1);
+ -webkit-backdrop-filter: blur(12px);
+ backdrop-filter: blur(12px);
+ border: 1px solid rgba(255, 255, 255, 0.15);
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1),
+ inset 0 1px 0 rgba(255, 255, 255, 0.2);
+ opacity: 1;
+}
+
+.custom-scrollbar::-webkit-scrollbar-thumb:hover {
+ background: rgba(255, 255, 255, 0.15);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15),
+ inset 0 1px 0 rgba(255, 255, 255, 0.3);
+}
+
+.custom-scrollbar::-webkit-scrollbar-corner {
+ background: transparent;
+}
+
+/* Dark mode scrollbar */
+.dark .custom-scrollbar {
+ scrollbar-color: transparent transparent;
+}
+
+.dark .custom-scrollbar:hover {
+ scrollbar-color: rgba(255, 255, 255, 0.1) rgba(0, 0, 0, 0.2);
+}
+
+.dark .custom-scrollbar:hover::-webkit-scrollbar-thumb,
+.dark .custom-scrollbar:active::-webkit-scrollbar-thumb {
+ background: rgba(255, 255, 255, 0.05);
+ -webkit-backdrop-filter: blur(12px);
+ backdrop-filter: blur(12px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ box-shadow: 0 4px 8px rgba(255, 255, 255, 0.05),
+ inset 0 1px 0 rgba(255, 255, 255, 0.1);
+}
+
+.dark .custom-scrollbar::-webkit-scrollbar-thumb:hover {
+ background: rgba(255, 255, 255, 0.1);
+ border: 1px solid rgba(255, 255, 255, 0.15);
+ box-shadow: 0 6px 12px rgba(255, 255, 255, 0.1),
+ inset 0 1px 0 rgba(255, 255, 255, 0.15);
+}
+
+/* Enhanced scrollbar with JavaScript visibility control */
+.custom-scrollbar.scrolling::-webkit-scrollbar-thumb,
+.custom-scrollbar.hover-scroll::-webkit-scrollbar-thumb {
+ background: rgba(255, 255, 255, 0.1);
+ -webkit-backdrop-filter: blur(12px);
+ backdrop-filter: blur(12px);
+ border: 1px solid rgba(255, 255, 255, 0.15);
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1),
+ inset 0 1px 0 rgba(255, 255, 255, 0.2);
+ opacity: 1;
+ transform: scaleY(1);
+}
+
+.custom-scrollbar.scrolling::-webkit-scrollbar-thumb:hover,
+.custom-scrollbar.hover-scroll::-webkit-scrollbar-thumb:hover {
+ background: rgba(255, 255, 255, 0.15);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15),
+ inset 0 1px 0 rgba(255, 255, 255, 0.3);
+}
+
+/* Dark mode enhanced scrollbar */
+.dark .custom-scrollbar.scrolling::-webkit-scrollbar-thumb,
+.dark .custom-scrollbar.hover-scroll::-webkit-scrollbar-thumb {
+ background: rgba(255, 255, 255, 0.05);
+ -webkit-backdrop-filter: blur(12px);
+ backdrop-filter: blur(12px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ box-shadow: 0 4px 8px rgba(255, 255, 255, 0.05),
+ inset 0 1px 0 rgba(255, 255, 255, 0.1);
+}
+
+.dark .custom-scrollbar.scrolling::-webkit-scrollbar-thumb:hover,
+.dark .custom-scrollbar.hover-scroll::-webkit-scrollbar-thumb:hover {
+ background: rgba(255, 255, 255, 0.1);
+ border: 1px solid rgba(255, 255, 255, 0.15);
+ box-shadow: 0 6px 12px rgba(255, 255, 255, 0.1),
+ inset 0 1px 0 rgba(255, 255, 255, 0.15);
+}
+
+/* Utility classes for common patterns */
+.scrollable-content {
+ overflow-y: auto;
+ scrollbar-width: thin;
+ scrollbar-color: transparent transparent;
+ transition: scrollbar-color 0.3s ease;
+}
+
+.scrollable-content:hover {
+ scrollbar-color: rgba(255, 255, 255, 0.2) rgba(255, 255, 255, 0.05);
+}
+
+.scrollable-content::-webkit-scrollbar {
+ width: 8px;
+}
+
+.scrollable-content::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.scrollable-content::-webkit-scrollbar-thumb {
+ background: transparent;
+ border-radius: 4px;
+ border: 1px solid transparent;
+ transition: all 0.3s ease;
+ opacity: 0;
+}
+
+.scrollable-content:hover::-webkit-scrollbar-thumb,
+.scrollable-content:active::-webkit-scrollbar-thumb {
+ background: rgba(255, 255, 255, 0.1);
+ -webkit-backdrop-filter: blur(12px);
+ backdrop-filter: blur(12px);
+ border: 1px solid rgba(255, 255, 255, 0.15);
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1),
+ inset 0 1px 0 rgba(255, 255, 255, 0.2);
+ opacity: 1;
+}
+
+.scrollable-content::-webkit-scrollbar-thumb:hover {
+ background: rgba(255, 255, 255, 0.15);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15),
+ inset 0 1px 0 rgba(255, 255, 255, 0.3);
+}
+
+/* Dark mode for scrollable content */
+.dark .scrollable-content {
+ scrollbar-color: transparent transparent;
+}
+
+.dark .scrollable-content:hover {
+ scrollbar-color: rgba(255, 255, 255, 0.1) rgba(0, 0, 0, 0.2);
+}
+
+.dark .scrollable-content:hover::-webkit-scrollbar-thumb,
+.dark .scrollable-content:active::-webkit-scrollbar-thumb {
+ background: rgba(255, 255, 255, 0.05);
+ -webkit-backdrop-filter: blur(12px);
+ backdrop-filter: blur(12px);
+ border: 1px solid rgba(255, 255, 255, 0.1);
+ box-shadow: 0 4px 8px rgba(255, 255, 255, 0.05),
+ inset 0 1px 0 rgba(255, 255, 255, 0.1);
+}
+
+.dark .scrollable-content::-webkit-scrollbar-thumb:hover {
+ background: rgba(255, 255, 255, 0.1);
+ border: 1px solid rgba(255, 255, 255, 0.15);
+ box-shadow: 0 6px 12px rgba(255, 255, 255, 0.1),
+ inset 0 1px 0 rgba(255, 255, 255, 0.15);
}
diff --git a/app/layout.tsx b/app/layout.tsx
index 3e2c078..1f039cf 100755
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -2,10 +2,18 @@ import type { Metadata } from "next";
import "./globals.css";
import { ThemeProvider } from "@/components/theme-provider";
import { Toaster } from "sonner";
+import QueryProvider from "@/components/QueryProvider";
+import { PerformanceMonitor } from "@/lib/performance";
+
+// Performance monitoring setup
+if (typeof window !== "undefined") {
+ PerformanceMonitor.logPageLoad();
+}
export const metadata: Metadata = {
title: "NebulaS3",
- description: "NebulaS3 is a simple and easy-to-use S3-compatible object storage service.",
+ description:
+ "NebulaS3 is a simple and easy-to-use S3-compatible object storage service.",
};
export default function RootLayout({
@@ -14,21 +22,25 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
-
-
-
-
-
+
+
+
+
+
+
{children}
-
-
-
-
+
+
+
+
+
);
}
diff --git a/app/loading.tsx b/app/loading.tsx
deleted file mode 100644
index e8c149c..0000000
--- a/app/loading.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import HomeHeroSkeleton from "@/components/custom-ui/skeleton/HomeHeroSkeleton";
-
-export default function Loading() {
- return ;
-}
diff --git a/app/not-found.tsx b/app/not-found.tsx
index c13c3d2..8b9de0a 100755
--- a/app/not-found.tsx
+++ b/app/not-found.tsx
@@ -1,37 +1,40 @@
-"use client";
-
+import { Metadata } from "next";
import React from "react";
+import Link from "next/link";
import Footer from "@/components/layout/Footer";
-import { useRouter } from "next/navigation";
-import CustomButton from "@/components/ui/custom-button";
import BackgroundShape from "@/components/custom-ui/home/BackgroundShape";
-export default function NotFound() {
- const router = useRouter();
-
- const handleBack = () => {
- router.push("/");
- };
+export const metadata: Metadata = {
+ title: "404 - Page Not Found | Nebula S3",
+ description: "The requested page couldn't be found. Please check the URL or navigate back to the Nebula S3 homepage.",
+ robots: "noindex, nofollow",
+};
+export default function NotFound() {
return (
-
-
-
-
-
- 404 - Page Not Found
-
-
- The requested page couldn't be found. Please check the URL or
- navigate back home.
-
+
+
+
+
+
+ 404 - Page Not Found
+
+
+ The requested page couldn't be found. Please check the URL or
+ navigate back home.
+
-
-
-
-
+
+
+ Back to Home
+
+
+
+
+
-
-
);
}
diff --git a/app/page.tsx b/app/page.tsx
index 1f58811..2dd5d4e 100755
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -1,9 +1,39 @@
-import dynamic from "next/dynamic";
+import { Metadata } from "next";
import BackgroundShape from "@/components/custom-ui/home/BackgroundShape";
import HomeHero from "@/components/custom-ui/home/HomeHero";
+import Footer from "@/components/layout/Footer";
+import Navbar from "@/components/layout/Navbar";
-const Footer = dynamic(() => import("@/components/layout/Footer"));
-const Navbar = dynamic(() => import("@/components/layout/Navbar"));
+export const metadata: Metadata = {
+ title: "Nebula S3 | Modern S3 Storage Management",
+ description: "NebulaS3 is a modern application that transforms how users interact with Amazon S3, offering a sophisticated web interface with enhanced capabilities.",
+ keywords: [
+ "S3",
+ "Amazon S3",
+ "Cloud Storage",
+ "Storage Management",
+ "Web Interface",
+ "Bucket Management",
+ "Object Storage",
+ "AWS",
+ ],
+ authors: [{ name: "Nebula S3 Team" }],
+ robots: "index, follow",
+ openGraph: {
+ type: "website",
+ locale: "en_US",
+ url: "https://nebula-s3.com",
+ title: "NEBULA S3 | Modern S3 Storage Management",
+ description: "NebulaS3 is a modern application that transforms how users interact with Amazon S3, offering a sophisticated web interface with enhanced capabilities.",
+ siteName: "NEBULA S3",
+ },
+ twitter: {
+ card: "summary_large_image",
+ title: "NEBULA S3 | Modern S3 Storage Management",
+ description: "NebulaS3 is a modern application that transforms how users interact with Amazon S3, offering a sophisticated web interface with enhanced capabilities.",
+ creator: "@nebulas3",
+ },
+};
export default function Home() {
return (
@@ -12,7 +42,6 @@ export default function Home() {
- {/*
*/}
diff --git a/components/ErrorBoundary.tsx b/components/ErrorBoundary.tsx
new file mode 100644
index 0000000..a954b6f
--- /dev/null
+++ b/components/ErrorBoundary.tsx
@@ -0,0 +1,73 @@
+"use client";
+
+import React from "react";
+
+interface ErrorBoundaryProps {
+ readonly children: React.ReactNode;
+ readonly fallback?: React.ComponentType<{ error: Error; retry: () => void }>;
+}
+
+interface ErrorBoundaryState {
+ hasError: boolean;
+ error: Error | null;
+}
+
+function DefaultErrorFallback({
+ error,
+ retry,
+}: {
+ error: Error;
+ retry: () => void;
+}) {
+ return (
+
+
+
⚠️
+
+ Something went wrong
+
+
+ {error.message ||
+ "An unexpected error occurred while loading the page."}
+
+
+
+
+ );
+}
+
+export class ErrorBoundary extends React.Component<
+ ErrorBoundaryProps,
+ ErrorBoundaryState
+> {
+ constructor(props: ErrorBoundaryProps) {
+ super(props);
+ this.state = { hasError: false, error: null };
+ }
+
+ static getDerivedStateFromError(error: Error): ErrorBoundaryState {
+ return { hasError: true, error };
+ }
+
+ componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
+ console.error("ErrorBoundary caught an error:", error, errorInfo);
+ }
+
+ retry = () => {
+ this.setState({ hasError: false, error: null });
+ };
+
+ render() {
+ if (this.state.hasError) {
+ const FallbackComponent = this.props.fallback || DefaultErrorFallback;
+ return ;
+ }
+
+ return this.props.children;
+ }
+}
diff --git a/components/HealthCheck.tsx b/components/HealthCheck.tsx
old mode 100755
new mode 100644
index b5e32ff..e997235
--- a/components/HealthCheck.tsx
+++ b/components/HealthCheck.tsx
@@ -1,26 +1,54 @@
"use client";
+
import { cn } from "@/lib/utils";
import React from "react";
-import LoadingStatus from "./custom-ui/status/LoadingStatus";
+import { useHealthCheck } from "@/hooks/useHealthCheck";
import SuccessStatus from "./custom-ui/status/SuccessStatus";
import ErrorStatus from "./custom-ui/status/ErrorStatus";
-import { useHealthCheck } from "@/lib/hooks/useHealthCheck";
interface HealthCheckProps {
readonly className?: string;
readonly compact?: boolean;
}
-const getStatusMessage = (status: string) => {
- if (status === "loading") return ;
- if (status === "SUCCESS") return ;
- return ;
-};
-
export default function HealthCheck({
+ className,
compact = false,
}: HealthCheckProps) {
- const { status } = useHealthCheck();
+ const { data: health, isLoading, error } = useHealthCheck();
+
+ if (isLoading) {
+ return (
+
+ );
+ }
+
+ if (error || !health) {
+ return (
+
+
+
+ );
+ }
return (
- {getStatusMessage(status)}
+
);
}
diff --git a/components/QueryProvider.tsx b/components/QueryProvider.tsx
new file mode 100644
index 0000000..0873e36
--- /dev/null
+++ b/components/QueryProvider.tsx
@@ -0,0 +1,47 @@
+"use client";
+
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
+import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
+import { useState } from "react";
+
+interface QueryProviderProps {
+ readonly children: React.ReactNode;
+}
+
+export default function QueryProvider({ children }: QueryProviderProps) {
+ const [queryClient] = useState(
+ () =>
+ new QueryClient({
+ defaultOptions: {
+ queries: {
+ staleTime: 60 * 1000, // 1 minute (reduced from 5 minutes)
+ gcTime: 5 * 60 * 1000, // 5 minutes (reduced from 10 minutes)
+ retry: (failureCount, error) => {
+ // Don't retry on 4xx errors
+ if (error && typeof error === "object" && "status" in error) {
+ const status = error.status as number;
+ if (status >= 400 && status < 500) return false;
+ }
+ return failureCount < 2; // Reduced from 3
+ },
+ refetchOnWindowFocus: false,
+ refetchOnReconnect: true, // Changed from 'always'
+ refetchOnMount: true, // Explicitly set
+ networkMode: "online", // Only run queries when online
+ },
+ mutations: {
+ retry: 1,
+ },
+ },
+ })
+ );
+
+ return (
+
+ {children}
+ {process.env.NODE_ENV === "development" && (
+
+ )}
+
+ );
+}
diff --git a/components/custom-ui/auth/login-form.tsx b/components/custom-ui/auth/login-form.tsx
old mode 100755
new mode 100644
index e2271f3..5fabcfd
--- a/components/custom-ui/auth/login-form.tsx
+++ b/components/custom-ui/auth/login-form.tsx
@@ -1,8 +1,10 @@
"use client";
import { cn } from "@/lib/utils";
-import { useState, useEffect } from "react";
-import { useAuth } from "@/lib/hooks/useAuth";
+import { useState } from "react";
+import { useLogin } from "@/hooks/useAuth";
+import type { LoginCredentials } from "@/types/auth";
+import { ButtonSpinner } from "@/components/ui/spinner";
// Inline minimal components to reduce imports
const Button = ({
@@ -46,105 +48,37 @@ const Input = ({
const Label = ({
className,
+ htmlFor,
...props
-}: React.LabelHTMLAttributes) => (
-
-);
-
-// Simple loading spinner instead of importing component
-const LoadingSpinner = () => (
-
-);
-
-// Minimalist redirecting component
-const Redirecting = () => (
-
-);
-
-// Loading state for form fields
-const InputSkeleton = () => (
-
-);
-
-const LabelSkeleton = () => (
-
-);
-
-const ButtonSkeleton = () => (
-
-);
+ );
+};
export default function LoginForm({
className,
...props
}: Readonly>) {
- const [credentials, setCredentials] = useState({
+ const [credentials, setCredentials] = useState({
accessKey: "",
secretKey: "",
region: "ap-south-1",
});
- const { login, loading, isRedirecting } = useAuth();
- const [isClientLoading, setIsClientLoading] = useState(true);
-
- // Simulate initial loading state using useEffect
- useEffect(() => {
- const timer = setTimeout(() => {
- setIsClientLoading(false);
- }, 800);
- return () => clearTimeout(timer);
- }, []);
+ const { mutate: login, isPending } = useLogin();
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
- await login(credentials);
+ login(credentials);
};
- if (isRedirecting) {
- return ;
- }
-
- // Show skeleton while client-side loading
- if (isClientLoading) {
- return (
-
- );
- }
-
return (