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 ( -
- -
{children}
-
- ); -} 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 ( -
- -
{children}
-
- ); -} 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 ( +
+
+
+ Checking status... +
+
+ ); + } + + 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) => ( -