diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 3722418..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": ["next/core-web-vitals", "next/typescript"] -} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..a298a12 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,7 @@ +name: Build +on: push +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 diff --git a/.gitignore b/.gitignore index e53191c..70ea34a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,42 +1,44 @@ -# Dependencies +# dependencies /node_modules /.pnp .pnp.js -# Testing +# testing /coverage -# Next.js +# next.js /.next/ /out/ -# Production +# production /build -# Misc +# misc .DS_Store *.pem -# Debug +# debug npm-debug.log* yarn-debug.log* yarn-error.log* -# Local env files +# local env files .env*.local .env -# Vercel +# vercel .vercel -# TypeScript +# typescript *.tsbuildinfo next-env.d.ts -# IDE -.idea -.vscode +# IDE files +.idea/ +.vscode/ +*.swp +*.swo -# Cache -.cache -.npm \ No newline at end of file +# logs +logs +*.log diff --git a/README.md b/README.md old mode 100644 new mode 100755 index 324c77e..e69de29 --- a/README.md +++ b/README.md @@ -1,52 +0,0 @@ -# 🌌 NebulaS3 - Advanced AWS S3 Management - -## 📋 Overview - -NebulaS3 is an advanced Amazon S3 management application providing secure interaction with AWS S3 storage. It simplifies bucket operations while ensuring robust authentication through AWS STS. - -## ⭐ Features - -- 🔐 Secure AWS STS Authentication -- 🪣 Create, List, and Delete S3 Buckets -- 🔑 Session-Based Authentication with Temporary Credentials -- 🖥️ User-Friendly Interface -- 🔄 Continuous Feature Updates - -## 🚀 Getting Started - -### 🔗 Quick Access - -**Live Application:** [https://nebulas3.vercel.app](https://nebulas3.vercel.app) - -### 🔒 Authentication - -- 📝 Enter AWS Access Key and Secret Key -- 🌎 Select AWS Region -- ⚡ System generates temporary session credentials via STS - -## 🛡️ Security - -- 🔐 AWS STS integration for secure authentication -- ⏳ Temporary session tokens -- 👥 IAM role-based access control -- 🚫 No credential storage - -## 🛠️ Technical Stack - -- 🎯 **Frontend:** Next.js 15 -- 🔧 **Backend:** Spring Boot (Java 17) -- ☁️ **AWS Services:** S3, STS -- 🔒 **Security:** Role-based authentication - -## 📚 Documentation - -Comprehensive guides and API references available on our [GitHub Wiki](https://github.com/xanderbilla/nebulas3/wiki) - -## 💬 Support - -- 🐛 GitHub Issues: [Create an issue](https://github.com/xanderbilla/nebulas3/issues) -- 📧 Email: [vikas99blr@gmail.com](mailto:vikas99blr@gmail.com) - -## 👨‍💻 Author - -[Vikas Singh](https://xanderbilla.com) diff --git a/app/(main)/buckets/layout.tsx b/app/(main)/buckets/layout.tsx new file mode 100644 index 0000000..c9ba126 --- /dev/null +++ b/app/(main)/buckets/layout.tsx @@ -0,0 +1,63 @@ +import { Metadata, Viewport } from "next"; +import dynamic from "next/dynamic"; +import DashboardNavSkeleton from "@/components/custom-ui/skeleton/DashboardNavSkeleton"; +import BackgroundShape from "@/components/custom-ui/home/BackgroundShape"; + +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, +}: { + readonly children: React.ReactNode; +}) { + return ( +
+ + +
+ {children} +
+
+ ); +} diff --git a/app/(main)/buckets/page.tsx b/app/(main)/buckets/page.tsx new file mode 100644 index 0000000..e7fdfb1 --- /dev/null +++ b/app/(main)/buckets/page.tsx @@ -0,0 +1,102 @@ +"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 ( +
+
+ }> + + + + }> + + + + }> + + +
+
+ ); +} diff --git a/app/(main)/dashboard/layout.tsx b/app/(main)/dashboard/layout.tsx new file mode 100755 index 0000000..9b61437 --- /dev/null +++ b/app/(main)/dashboard/layout.tsx @@ -0,0 +1,57 @@ +import { Metadata, Viewport } from "next"; +import dynamic from "next/dynamic"; +import DashboardNavSkeleton from "@/components/custom-ui/skeleton/DashboardNavSkeleton"; +import BackgroundShape from "@/components/custom-ui/home/BackgroundShape"; +import Footer from "@/components/layout/Footer"; + +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, +}: { + readonly children: React.ReactNode; +}) { + return ( +
+ + +
{children}
+
+
+ ); +} diff --git a/app/(main)/dashboard/page.tsx b/app/(main)/dashboard/page.tsx new file mode 100755 index 0000000..029f2a4 --- /dev/null +++ b/app/(main)/dashboard/page.tsx @@ -0,0 +1,15 @@ +import { Suspense } from "react"; +import DashboardSkeleton from "@/components/custom-ui/skeleton/DashboardSkeleton"; +import DashboardContent from "@/components/custom-ui/dashboard/DashboardContent"; + +export default function DashboardPage() { + return ( + <> +
+ }> + + +
+ + ); +} diff --git a/app/(pages)/(public-pages)/about/layout.tsx b/app/(pages)/about/layout.tsx old mode 100644 new mode 100755 similarity index 78% rename from app/(pages)/(public-pages)/about/layout.tsx rename to app/(pages)/about/layout.tsx index d3ba552..4a8b41a --- a/app/(pages)/(public-pages)/about/layout.tsx +++ b/app/(pages)/about/layout.tsx @@ -1,42 +1,48 @@ -import { Metadata } from "next"; -import React from "react"; - -interface AboutLayoutProps { - 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}; -} +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)/(public-pages)/about/page.tsx b/app/(pages)/about/page.tsx old mode 100644 new mode 100755 similarity index 72% rename from app/(pages)/(public-pages)/about/page.tsx rename to app/(pages)/about/page.tsx index 61cadb9..b5eb8d2 --- a/app/(pages)/(public-pages)/about/page.tsx +++ b/app/(pages)/about/page.tsx @@ -1,54 +1,57 @@ -"use client"; - -import React from "react"; -import { ArrowLeft } from "lucide-react"; -import { useRouter } from "next/navigation"; -export default function Page() { - const router = useRouter(); - - const handleBack = () => { - router.back(); - }; - - return ( -
-
- - Back to Home -
- -
-

- About NebulaS3 -

- -
-

- Welcome to our advanced storage solution built on the foundation of - S3. I've taken the reliable features you love from S3 and - enhanced them with modern capabilities that today's - applications and user demand. -

- -
-

Key Features:

-
    -
  • - ✓ All standard S3 functionalities -
  • -
  • - ✓ Real-time data processing -
  • -
  • - + More features coming soon... -
  • -
-
-
-
-
- ); -} +"use client"; + +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(); + }; + + return ( +
+
+ } + /> +
+ +
+

+ About NebulaS3 +

+ +
+

+ Welcome to our advanced storage solution built on the foundation of + S3. I've taken the reliable features you love from S3 and + enhanced them with modern capabilities that today's + applications and user demand. +

+ +
+

Key Features:

+
    +
  • + ✓ All standard S3 functionalities +
  • +
  • + ✓ Real-time data processing +
  • +
  • + + More features coming soon... +
  • +
+
+
+
+
+ ); +} diff --git a/app/(pages)/(public-pages)/contact/layout.tsx b/app/(pages)/contact/layout.tsx old mode 100644 new mode 100755 similarity index 73% rename from app/(pages)/(public-pages)/contact/layout.tsx rename to app/(pages)/contact/layout.tsx index fb551e8..ec76a71 --- a/app/(pages)/(public-pages)/contact/layout.tsx +++ b/app/(pages)/contact/layout.tsx @@ -1,41 +1,47 @@ -import { Metadata } from "next"; -import React from "react"; - -interface AboutLayoutProps { - 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 AboutLayout({ children }: AboutLayoutProps) { - return children; -} +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)/(public-pages)/contact/page.tsx b/app/(pages)/contact/page.tsx old mode 100644 new mode 100755 similarity index 66% rename from app/(pages)/(public-pages)/contact/page.tsx rename to app/(pages)/contact/page.tsx index c805eab..e432626 --- a/app/(pages)/(public-pages)/contact/page.tsx +++ b/app/(pages)/contact/page.tsx @@ -1,49 +1,45 @@ -"use client"; - -import React from "react"; -import { ArrowLeft } from "lucide-react"; -import { useRouter } from "next/navigation"; -import Link from "next/link"; -type Props = object; - -export default function Page({}: Props) { - const router = useRouter(); - - const handleBack = () => { - router.back(); - }; - - return ( -
-
- - Back to Home -
- -
-

- Contact Us -

- -
-

- Have questions or need support? We're here to help. Reach out - to us through any of these channels: -

- -
-
-

Email Support

- - mail.vikas99blr@gmail.com - -
-
-
-
-
- ); -} +"use client"; + +import React from "react"; +import { useRouter } from "next/navigation"; +import Link from "next/link"; +import CustomButton from "@/components/ui/custom-button"; +import { ArrowLeft } from "lucide-react"; + +export default function Page() { + const router = useRouter(); + + const handleBack = () => { + router.back(); + }; + + return ( +
+
+ } /> +
+ +
+

+ Contact Us +

+ +
+

+ Have questions or need support? We're here to help. Reach out + to us through any of these channels: +

+ +
+
+

Email Support

+ + mail.vikas99blr@gmail.com + +
+
+
+
+
+ ); +} diff --git a/app/(pages)/dashboard/DashboardLayout.tsx b/app/(pages)/dashboard/DashboardLayout.tsx deleted file mode 100644 index 1963c33..0000000 --- a/app/(pages)/dashboard/DashboardLayout.tsx +++ /dev/null @@ -1,77 +0,0 @@ -"use client"; - -import { - SidebarInset, - SidebarProvider, - SidebarTrigger, -} from "@/components/ui/sidebar"; -import { AppSidebar } from "@/components/app-sidebar"; -import { - Breadcrumb, - BreadcrumbItem, - BreadcrumbLink, - BreadcrumbList, - BreadcrumbPage, - BreadcrumbSeparator, -} from "@/components/ui/breadcrumb"; -import { Separator } from "@/components/ui/separator"; -import React from "react"; -import { usePathname } from "next/navigation"; - -interface LoginLayoutProps { - children: React.ReactNode; -} - -const Breadcrumbs = () => { - const pathname = usePathname(); - const paths = pathname.split("/").filter(Boolean); - - return ( - - - {paths.map((path: string, index: number) => { - const fullPath = "/" + paths.slice(0, index + 1).join("/"); - const isLast = index === paths.length - 1; - return ( - - - {isLast ? ( - - {path.charAt(0).toUpperCase() + path.slice(1)} - - ) : ( - - {path.charAt(0).toUpperCase() + path.slice(1)} - - )} - - {!isLast && } - - ); - })} - - - ); -}; - -const DashboardLayout: React.FC = ({ children }) => { - return ( -
- - - -
-
- - - -
-
-
{children}
-
-
-
- ); -}; - -export default DashboardLayout; diff --git a/app/(pages)/dashboard/buckets/[bucket-name]/page.tsx b/app/(pages)/dashboard/buckets/[bucket-name]/page.tsx deleted file mode 100644 index e63d32a..0000000 --- a/app/(pages)/dashboard/buckets/[bucket-name]/page.tsx +++ /dev/null @@ -1,277 +0,0 @@ -"use client"; - -import React from "react"; -import { Button } from "@/components/ui/button"; -import { Upload, Check, ChevronsUpDown } from "lucide-react"; -import dynamic from "next/dynamic"; - -import { - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList, -} from "@/components/ui/command"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover"; -import { cn } from "@/lib/utils"; -import { S3File, S3Root } from "@/types/S3Objects"; - -type FileType = - | "document" - | "compressed" - | "image" - | "audio" - | "video" - | "unknown" - | "folder"; - -const mockFileData: S3Root = { - folders: [ - { - folders: [], - name: "compressed", - files: [ - { - extension: "rar", - etag: '"dd9bb762731449c0e06a2c869b739db1"', - size: "1.56 MB", - type: "compressed", - name: "lab.rar", - location: "compressed/lab.rar", - lastModified: "2025-03-08T05:15:12Z", - }, - ], - location: "compressed/", - type: "folder", - itemCount: 1, - }, - { - folders: [], - name: "documents", - files: [ - { - extension: "pdf", - etag: '"52e419c0508aaeea9213f513b84e8d73"', - size: "9.17 KB", - type: "document", - name: "rptTimeTableStudent.pdf", - location: "documents/rptTimeTableStudent.pdf", - lastModified: "2025-03-08T05:15:11Z", - }, - ], - location: "documents/", - type: "folder", - itemCount: 1, - }, - { - folders: [], - name: "videos", - files: [ - { - extension: "mp4", - etag: '"447b7f6a669fc87a1e5c1d506474a7cb"', - size: "1.12 MB", - type: "video", - name: "VID_4556446.mp4", - location: "videos/VID_4556446.mp4", - lastModified: "2025-03-08T05:15:12Z", - }, - { - extension: "mp4", - etag: '"4aa28625598a7adfb7ec3fef5e44453b-2"', - size: "31.48 MB", - type: "video", - name: "VID_5468553.mp4", - location: "videos/VID_5468553.mp4", - lastModified: "2025-03-08T05:15:11Z", - }, - ], - location: "videos/", - type: "folder", - itemCount: 2, - }, - ], - name: "root", - files: [ - { - extension: "jpg", - etag: '"8aa445cbeca5119ec77c4e4bb760e12e"', - size: "130.04 KB", - type: "image", - name: "IMG_2135545.jpg", - location: "IMG_2135545.jpg", - lastModified: "2025-03-08T05:15:13Z", - }, - { - extension: "jpg", - etag: '"3ee4b3bb7fe145c77a883db5b201a172"', - size: "93.66 KB", - type: "image", - name: "IMG_2135546.jpg", - location: "IMG_2135546.jpg", - lastModified: "2025-03-08T05:15:13Z", - }, - ], - location: "", - type: "folder", - itemCount: 5, -}; - -const fileTypes = [ - { value: "all", label: "All Files" }, - { value: "document", label: "Documents" }, - { value: "compressed", label: "Compressed" }, - { value: "image", label: "Images" }, - { value: "audio", label: "Audio" }, - { value: "video", label: "Video" }, - { value: "folder", label: "Folders" }, -]; - -const Files = dynamic(() => import("@/components/files-component/files")); -const SearchBar = dynamic(() => import("@/components/bucket-component/search")); -const FilterButtons = dynamic( - () => import("@/components/bucket-component/filter") -); - -export default function Page() { - const [searchTerm, setSearchTerm] = React.useState(""); - const [activeFilter, setActiveFilter] = React.useState< - "size" | "date" | null - >(null); - const [open, setOpen] = React.useState(false); - const [selectedType, setSelectedType] = React.useState("all"); - - const filteredAndSortedData = React.useMemo(() => { - const filterItems = (items: S3File[] | S3Root["folders"]) => { - return items.filter((item) => - item.name.toLowerCase().includes(searchTerm.toLowerCase()) - ); - }; - - let filteredFiles = filterItems(mockFileData.files); - const filteredFolders = filterItems(mockFileData.folders); - - // Apply type filter - if (selectedType !== "all") { - filteredFiles = filteredFiles.filter((file) => file.type === selectedType); - } - - // Apply date or size filter only to files - if (activeFilter === "date") { - filteredFiles = [...filteredFiles].sort((a, b) => { - if ('lastModified' in a && 'lastModified' in b) { - return new Date(b.lastModified).getTime() - new Date(a.lastModified).getTime(); - } - return 0; - }); - } else if (activeFilter === "size") { - const convertToBytes = (size: string) => { - const num = parseFloat(size); - if (size.includes("GB")) return num * 1024 * 1024 * 1024; - if (size.includes("MB")) return num * 1024 * 1024; - if (size.includes("KB")) return num * 1024; - return num; - }; - filteredFiles = [...filteredFiles].sort((a, b) => { - if ('size' in a && 'size' in b) { - return convertToBytes(b.size) - convertToBytes(a.size); - } - return 0; - }); - } - - return { - files: filteredFiles, - folders: filteredFolders, - }; - }, [searchTerm, activeFilter, selectedType]); - - - - return ( -
-
-
- -
-
- - - - - - - - - - - No results found. - - - {fileTypes.map((type) => ( - { - setSelectedType(currentValue); - setOpen(false); - }} - className="dark:text-gray-100 dark:hover:bg-gray-700" - > - {type.label} - - - ))} - - - - - - -
-
-
- {filteredAndSortedData.folders.map((folder) => ( - - ))} - {filteredAndSortedData.files.map((file) => ( - - ))} -
-
- ); -} diff --git a/app/(pages)/dashboard/buckets/layout.tsx b/app/(pages)/dashboard/buckets/layout.tsx deleted file mode 100644 index dfa26b8..0000000 --- a/app/(pages)/dashboard/buckets/layout.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Metadata } from "next"; -import React from "react"; - -export const metadata:Metadata = { - title: "NebulaS3 Buckets - Manage Your AWS S3 Buckets Easily", - description: - "Effortlessly create, list, and manage your AWS S3 buckets with NebulaS3. Securely store and organize your cloud storage using advanced AWS integrations.", - keywords: [ - "NebulaS3 Buckets", - "AWS S3 Bucket Management", - "Create S3 Bucket", - "List S3 Buckets", - "Manage AWS Storage", - "Cloud Storage Buckets", - "S3 Object Storage", - "AWS Cloud Storage", - ], - openGraph: { - title: "NebulaS3 Buckets - Manage Your AWS S3 Buckets", - description: - "Simplify your AWS S3 bucket management with NebulaS3's powerful and secure interface.", - url: "https://nebulas3.vercel.app/dashboard/buckets", - type: "website", - images: [ - { - url: "https://nebulas3.vercel.app/images/buckets-og.jpg", - width: 1200, - height: 630, - alt: "NebulaS3 Buckets - AWS S3 Management", - }, - ], - }, - }; - - -export default function BucketsLayout({ - children, -}: { - children: React.ReactNode; -}) { - return children; -} diff --git a/app/(pages)/dashboard/buckets/page.tsx b/app/(pages)/dashboard/buckets/page.tsx deleted file mode 100644 index dff3b7f..0000000 --- a/app/(pages)/dashboard/buckets/page.tsx +++ /dev/null @@ -1,109 +0,0 @@ -"use client"; - -import { useState, useMemo } from "react"; -import dynamic from "next/dynamic"; -import { CreateBucket } from "@/components/bucket-component/create-bkt"; -import useBuckets from "@/hooks/useBuckets"; - -// Dynamically imported components with loading placeholders -const SearchBar = dynamic( - () => import("@/components/bucket-component/search"), - { - loading: () => ( -
- ), - } -); -const FilterButtons = dynamic( - () => import("@/components/bucket-component/filter"), - { - loading: () =>
, - } -); -const BucketCard = dynamic( - () => import("@/components/bucket-component/bucket-card"), - { - loading: () =>
, - } -); -const SelectRegions = dynamic( - () => import("@/components/bucket-component/regions"), - { - loading: () =>
, - } -); - -export default function Page() { - const { buckets, isLoading, isError, refetch } = useBuckets(); - const [activeFilter, setActiveFilter] = useState<"size" | "date" | null>( - null - ); - const [searchTerm, setSearchTerm] = useState(""); - - // Compute the displayed buckets based on the search term and active filter - const displayedBuckets = useMemo(() => { - let data = [...buckets]; - - if (searchTerm) { - data = data.filter((bucket) => - bucket.bucketName.toLowerCase().includes(searchTerm.toLowerCase()) - ); - } - if (activeFilter === "size") { - data = data.sort((a, b) => { - const sizeToBytes = (size: string) => { - const [value, unit] = size.split(" "); - const numValue = parseFloat(value); - switch (unit?.toUpperCase()) { - case "KB": - return numValue * 1024; - case "MB": - return numValue * 1024 * 1024; - case "GB": - return numValue * 1024 * 1024 * 1024; - case "TB": - return numValue * 1024 * 1024 * 1024 * 1024; - default: - return numValue; - } - }; - return sizeToBytes(b.size || "0") - sizeToBytes(a.size || "0"); - }); - } else if (activeFilter === "date") { - data = data.sort( - (a, b) => - new Date(b.createdOn).getTime() - new Date(a.createdOn).getTime() - ); - } - return data; - }, [buckets, searchTerm, activeFilter]); - - return ( -
-
- - - - -
- {isLoading ? ( -
- {[...Array(4)].map((_, index) => ( -
- ))} -
- ) : isError ? ( -

{isError}

- ) : ( -
- {displayedBuckets.map((bucket) => ( - - ))} -
- )} -
- ); -} diff --git a/app/(pages)/dashboard/layout.tsx b/app/(pages)/dashboard/layout.tsx deleted file mode 100644 index ea509d5..0000000 --- a/app/(pages)/dashboard/layout.tsx +++ /dev/null @@ -1,37 +0,0 @@ -export const metadata:Metadata = { - title: "NebulaS3 Dashboard - Manage Your AWS S3 Buckets Effortlessly", - description: - "Access your NebulaS3 dashboard to manage AWS S3 buckets with advanced features, secure authentication, and seamless cloud storage integration.", - keywords: [ - "NebulaS3 Dashboard", - "AWS S3 Management", - "S3 Cloud Storage", - "Secure S3 Access", - "Amazon S3 Buckets", - "Cloud Storage Dashboard", - "AWS S3 SaaS", - "S3 File Management", - ], - openGraph: { - title: "NebulaS3 Dashboard - Manage Your AWS S3 Buckets", - description: - "Easily manage and organize your AWS S3 storage with NebulaS3's secure and intuitive dashboard.", - url: "https://nebulas3.vercel.app/dashboard", - type: "website", - images: [ - { - url: "https://nebulas3.vercel.app/images/dashboard-og.jpg", - width: 1200, - height: 630, - alt: "NebulaS3 Dashboard - AWS S3 Management", - }, - ], - }, -}; - -import { Metadata } from "next"; -import DashboardLayout from "./DashboardLayout"; - -export default function Layout({ children }: { children: React.ReactNode }) { - return {children}; -} diff --git a/app/(pages)/(public-pages)/layout.tsx b/app/(pages)/layout.tsx old mode 100644 new mode 100755 similarity index 59% rename from app/(pages)/(public-pages)/layout.tsx rename to app/(pages)/layout.tsx index f4b43c5..09a0c8d --- a/app/(pages)/(public-pages)/layout.tsx +++ b/app/(pages)/layout.tsx @@ -1,17 +1,17 @@ -import Footer from "@/components/footer"; -import Navbar from "@/components/home-page/navbar"; -import React from "react"; - -export default function PublicPagesLayout({ - children, -}: { - children: React.ReactNode; -}) { - return ( -
- - {children} -
-
- ); -} +import Footer from "@/components/layout/Footer"; +import Navbar from "@/components/layout/Navbar"; +import React from "react"; + +export default function PublicPagesLayout({ + children, +}: { + readonly children: React.ReactNode; +}) { + return ( +
+ + {children} +
+
+ ); +} diff --git a/app/(pages)/login/components/login-form.tsx b/app/(pages)/login/components/login-form.tsx deleted file mode 100644 index 9775cd2..0000000 --- a/app/(pages)/login/components/login-form.tsx +++ /dev/null @@ -1,138 +0,0 @@ -"use client"; - -import { cn } from "@/lib/utils"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { useState } from "react"; -import { useRouter } from "next/navigation"; -import { toast } from "sonner"; -import SpinnerIcon from "@/icons/spinner-icon"; -import Redirecting from "@/skeleton/redirecting"; - -export default function LoginForm({ - className, - ...props -}: React.ComponentPropsWithoutRef<"form">) { - const [credentials, setCredentials] = useState({ - accessKey: "", - secretKey: "", - region: "ap-south-1", - }); - const [loading, setLoading] = useState(false); - const [isRedirecting, setIsRedirecting] = useState(false); - const router = useRouter(); - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - setLoading(true); - - try { - const response = await fetch( - `${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/login`, - { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(credentials), - } - ); - - const data = await response.json(); - - if (response.ok && data.status === "SUCCESS" && data.data.sessionToken) { - document.cookie = `sessionToken=${data.data.sessionToken}; path=/; Secure; SameSite=Strict`; - toast.success("Login successful!"); - setIsRedirecting(true); - setTimeout(() => { - router.push("/dashboard/buckets"); - }, 200); - } else { - toast.error(data.message || "Invalid credentials"); - } - } catch (error) { - console.error("Error:", error); - toast.error("Failed to validate credentials"); - } finally { - setLoading(false); - } - }; - - if (isRedirecting) { - return ; - } - - return ( -
-
-

- AWS Credentials -

-
-
-
- - - setCredentials((prev) => ({ - ...prev, - accessKey: e.target.value, - })) - } - /> -
-
- - - setCredentials((prev) => ({ - ...prev, - secretKey: e.target.value, - })) - } - /> -
- -
-
- ); -} diff --git a/app/(pages)/login/layout.tsx b/app/(pages)/login/layout.tsx deleted file mode 100644 index a4ad834..0000000 --- a/app/(pages)/login/layout.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { Metadata } from "next"; -import React from "react"; - -interface LoginLayoutProps { - children: React.ReactNode; -} - -export const metadata:Metadata = { - title: "Login to NebulaS3 - Secure AWS S3 Access", - description: - "Access your NebulaS3 account securely. Log in to manage your AWS S3 buckets with advanced features and seamless authentication.", - keywords: [ - "NebulaS3 Login", - "AWS S3 Login", - "Secure S3 Access", - "Cloud Storage Authentication", - "Amazon S3 Management", - "S3 SaaS Platform", - "S3 Dashboard Login", - "AWS Cloud Storage", - ], - openGraph: { - title: "Login to NebulaS3 - Secure AWS S3 Access", - description: - "Sign in to your NebulaS3 account and securely manage AWS S3 buckets with role-based authentication.", - url: "https://nebulas3.vercel.app/login", - type: "website", - images: [ - { - url: "https://nebulas3.vercel.app/images/login-og.jpg", - width: 1200, - height: 630, - alt: "Login to NebulaS3 - Secure AWS S3 Access", - }, - ], - }, -}; - - -export default function LoginLayout({ children }: LoginLayoutProps) { - return
{children}
; -} diff --git a/app/(pages)/login/page.tsx b/app/(pages)/login/page.tsx deleted file mode 100644 index 3441e44..0000000 --- a/app/(pages)/login/page.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { GalleryVerticalEnd } from "lucide-react"; -import dynamic from "next/dynamic"; -import Image from "next/image"; -import Link from "next/link"; -import { LoginInfo } from "./components/login-info"; - -const LoginForm = dynamic(() => import("./components/login-form"), { - loading: () => ( -
- ), -}); - -export default function LoginPage() { - return ( -
-
-
- -
- -
- NebulaS3 - -
- -
-
- -
-
-
-
- Image -
-
- ); -} diff --git a/app/api/auth/login/route.ts b/app/api/auth/login/route.ts new file mode 100644 index 0000000..e9862a5 --- /dev/null +++ b/app/api/auth/login/route.ts @@ -0,0 +1,21 @@ +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 new file mode 100644 index 0000000..7d190d5 --- /dev/null +++ b/app/api/health/route.ts @@ -0,0 +1,18 @@ +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 new file mode 100755 index 0000000..9c1b280 --- /dev/null +++ b/app/auth/login/layout.tsx @@ -0,0 +1,20 @@ +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/loading.tsx b/app/auth/login/loading.tsx new file mode 100644 index 0000000..c6be564 --- /dev/null +++ b/app/auth/login/loading.tsx @@ -0,0 +1,6 @@ +import AuthPageSkeleton from "@/components/custom-ui/skeleton/AuthPageSkeleton"; +import "@/styles/shimmer.css"; + +export default function Loading() { + return ; +} diff --git a/app/auth/login/page.tsx b/app/auth/login/page.tsx new file mode 100755 index 0000000..fd275b5 --- /dev/null +++ b/app/auth/login/page.tsx @@ -0,0 +1,84 @@ +"use client"; + +import { ArrowLeft } from "lucide-react"; +import dynamic from "next/dynamic"; +import Link from "next/link"; +import BackButton from "@/components/ui/custom-button"; + +// Optimize logo component to reduce imports +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 ( +
+
+
+ + + NebulaS3 + +
+
+ window.history.back()} + icon={} + /> +
+
+
+ +
+
+
+
+ {/* Replaced Image with CSS background for optimization */} +
+
+
+ ); +} diff --git a/app/favicon.ico b/app/favicon.ico old mode 100644 new mode 100755 index 64e78ec..718d6fe Binary files a/app/favicon.ico and b/app/favicon.ico differ diff --git a/app/fonts/GeistMonoVF.woff b/app/fonts/GeistMonoVF.woff deleted file mode 100644 index f2ae185..0000000 Binary files a/app/fonts/GeistMonoVF.woff and /dev/null differ diff --git a/app/fonts/GeistVF.woff b/app/fonts/GeistVF.woff deleted file mode 100644 index 1b62daa..0000000 Binary files a/app/fonts/GeistVF.woff and /dev/null differ diff --git a/app/globals.css b/app/globals.css index b05c379..3ce8907 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,148 +1,292 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; +@import "tailwindcss"; +@import "tw-animate-css"; -body { - font-family: Arial, Helvetica, sans-serif; +@custom-variant dark (&:is(.dark *)); + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); + --color-ring: var(--ring); + --color-input: var(--input); + --color-border: var(--border); + --color-destructive: var(--destructive); + --color-accent-foreground: var(--accent-foreground); + --color-accent: var(--accent); + --color-muted-foreground: var(--muted-foreground); + --color-muted: var(--muted); + --color-secondary-foreground: var(--secondary-foreground); + --color-secondary: var(--secondary); + --color-primary-foreground: var(--primary-foreground); + --color-primary: var(--primary); + --color-popover-foreground: var(--popover-foreground); + --color-popover: var(--popover); + --color-card-foreground: var(--card-foreground); + --color-card: var(--card); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); } -@layer utilities { - .text-balance { - text-align: justify; - -webkit-hyphens: auto; - hyphens: auto; - } +:root { + --radius: 0.625rem; + + --background: #ffffff; + --foreground: #000000; + + --card: rgba(255, 255, 255, 0.8); + --card-foreground: #000000; + + /* 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; + + --popover: #ffffff; + --popover-foreground: #000000; + + --primary: #000000; + --primary-foreground: #ffffff; + + --secondary: #f3f3f3; + --secondary-foreground: #000000; + + --muted: #f6f6f6; + --muted-foreground: #666666; + + --accent: #e0e0e0; + --accent-foreground: #000000; + + --destructive: #ff4d4f; + + --border: #e0e0e0; + --input: #e0e0e0; + --ring: #cccccc; + + --chart-1: #ff6b6b; + --chart-2: #4dabf7; + --chart-3: #9775fa; + --chart-4: #51cf66; + --chart-5: #fcc419; + + --sidebar: #ffffff; + --sidebar-foreground: #000000; + --sidebar-primary: #000000; + --sidebar-primary-foreground: #ffffff; + --sidebar-accent: #f3f3f3; + --sidebar-accent-foreground: #000000; + --sidebar-border: #e0e0e0; + --sidebar-ring: #cccccc; } -@layer base { - :root { - --background: 0 0% 100%; - --foreground: 240 10% 3.9%; - --card: 0 0% 100%; - --card-foreground: 240 10% 3.9%; - --popover: 0 0% 100%; - --popover-foreground: 240 10% 3.9%; - --primary: 240 5.9% 10%; - --primary-foreground: 0 0% 98%; - --secondary: 240 4.8% 95.9%; - --secondary-foreground: 240 5.9% 10%; - --muted: 240 4.8% 95.9%; - --muted-foreground: 240 3.8% 46.1%; - --accent: 240 4.8% 95.9%; - --accent-foreground: 240 5.9% 10%; - --destructive: 0 84.2% 60.2%; - --destructive-foreground: 0 0% 98%; - --border: 240 5.9% 90%; - --input: 240 5.9% 90%; - --ring: 240 10% 3.9%; - --chart-1: 12 76% 61%; - --chart-2: 173 58% 39%; - --chart-3: 197 37% 24%; - --chart-4: 43 74% 66%; - --chart-5: 27 87% 67%; - --radius: 0.5rem; - --sidebar-background: 0 0% 98%; - --sidebar-foreground: 240 5.3% 26.1%; - --sidebar-primary: 240 5.9% 10%; - --sidebar-primary-foreground: 0 0% 98%; - --sidebar-accent: 240 4.8% 95.9%; - --sidebar-accent-foreground: 240 5.9% 10%; - --sidebar-border: 220 13% 91%; - --sidebar-ring: 217.2 91.2% 59.8%; - } - .dark { - --background: 240 10% 3.9%; - --foreground: 0 0% 98%; - --card: 240 10% 3.9%; - --card-foreground: 0 0% 98%; - --popover: 240 10% 3.9%; - --popover-foreground: 0 0% 98%; - --primary: 0 0% 98%; - --primary-foreground: 240 5.9% 10%; - --secondary: 240 3.7% 15.9%; - --secondary-foreground: 0 0% 98%; - --muted: 240 3.7% 15.9%; - --muted-foreground: 240 5% 64.9%; - --accent: 240 3.7% 15.9%; - --accent-foreground: 0 0% 98%; - --destructive: 0 62.8% 30.6%; - --destructive-foreground: 0 0% 98%; - --border: 240 3.7% 15.9%; - --input: 240 3.7% 15.9%; - --ring: 240 4.9% 83.9%; - --chart-1: 220 70% 50%; - --chart-2: 160 60% 45%; - --chart-3: 30 80% 55%; - --chart-4: 280 65% 60%; - --chart-5: 340 75% 55%; - --sidebar-background: 240 5.9% 10%; - --sidebar-foreground: 240 4.8% 95.9%; - --sidebar-primary: 224.3 76.3% 48%; - --sidebar-primary-foreground: 0 0% 100%; - --sidebar-accent: 240 3.7% 15.9%; - --sidebar-accent-foreground: 240 4.8% 95.9%; - --sidebar-border: 240 3.7% 15.9%; - --sidebar-ring: 217.2 91.2% 59.8%; - } +.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; + + --border: #333333; + --input: #333333; + --ring: #666666; + + --chart-1: #f03e3e; + --chart-2: #228be6; + --chart-3: #845ef7; + --chart-4: #40c057; + --chart-5: #fab005; + + --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; } @layer base { * { - @apply border-border; + @apply border-border outline-ring/50; } body { @apply bg-background text-foreground; } } -/* Custom Scrollbar Styles */ -/* Light mode scrollbar */ -::-webkit-scrollbar { - width: 1px; - height: 1px; -} +@layer components { + .sidebar { + @apply bg-sidebar text-sidebar-foreground border-border; + } + .sidebar-header { + @apply bg-sidebar text-sidebar-primary-foreground border-border; + } + .sidebar-item { + @apply bg-sidebar-accent text-sidebar-accent-foreground; + } + .sidebar-item:hover { + @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; + } + .glass-nav::before { + content: ""; + position: absolute; + inset: 0; + background: radial-gradient( + ellipse at top right, + rgba(255, 255, 255, 0.15), + transparent 70% + ); + pointer-events: none; + } + .glass-text { + @apply text-black/90 dark:text-white/90; + } + .glass-hover { + @apply hover:bg-white/30 dark:hover:bg-white/10 hover:text-black 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); + } + .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; + } + .glass-card-title { + @apply text-black/80 dark:text-white/90 font-medium; + } + .glass-card-description { + @apply text-black/60 dark:text-white/60 text-sm; + } + .glass-card-content { + @apply text-black/70 dark:text-white/80; + } + .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; + } + .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; + } + @keyframes card-title-underline { + 0% { + width: 0%; + left: 0; + right: 100%; + } + 50% { + width: 100%; + left: 0; + right: 0; + } + 100% { + width: 100%; + left: 0; + right: 0; + } + } -::-webkit-scrollbar-track { - background: hsl(var(--background)); - border-radius: 1px; -} + .card-title-animation:hover::after { + animation: card-title-underline 0.8s ease-in-out forwards; + } -::-webkit-scrollbar-thumb { - background: hsl(var(--muted-foreground)); - border-radius: 1px; + .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; + } + .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; + } + .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; + } + .glass-active { + @apply bg-white/50 dark:bg-white/10 shadow-sm shadow-purple-300/20 dark:shadow-purple-800/10; + } + /* Glass card styles now defined directly in the component with Tailwind */ } -::-webkit-scrollbar-thumb:hover { - background: hsl(var(--accent-foreground)); +/* Skeleton Shimmer Effect */ +.shimmer { + position: relative; + overflow: hidden; } -/* Dark mode scrollbar */ -.dark ::-webkit-scrollbar { - width: 1px; - height: 1px; +.shimmer::after { + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + transform: translateX(-100%); + background-image: linear-gradient( + 90deg, + rgba(255, 255, 255, 0) 0, + rgba(255, 255, 255, 0.2) 20%, + rgba(255, 255, 255, 0.5) 60%, + rgba(255, 255, 255, 0) + ); + animation: shimmer 2s infinite; } -.dark ::-webkit-scrollbar-track { - background: hsl(var(--background)); - border-radius: 1px; +@keyframes shimmer { + 100% { + transform: translateX(100%); + } } -.dark ::-webkit-scrollbar-thumb { - background: hsl(var(--muted-foreground)); - border-radius: 1px; +/* Dark mode shimmer */ +.dark .shimmer::after { + background-image: linear-gradient( + 90deg, + rgba(255, 255, 255, 0) 0, + rgba(255, 255, 255, 0.05) 20%, + rgba(255, 255, 255, 0.1) 60%, + rgba(255, 255, 255, 0) + ); } -.dark ::-webkit-scrollbar-thumb:hover { - background: hsl(var(--accent-foreground)); +:root { + scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* IE and Edge */ +} +:root::-webkit-scrollbar { + display: none; /* Chrome, Safari, Opera */ } - - - -@layer base { - * { - @apply border-border outline-ring/50; - } - body { - @apply bg-background text-foreground; - } -} \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx old mode 100644 new mode 100755 index e512bb6..3e2c078 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,44 +1,11 @@ -import { Poppins } from "next/font/google"; +import type { Metadata } from "next"; import "./globals.css"; -import { Toaster } from "@/components/ui/sonner" -import ThemeToggle from "@/components/theme-toggle"; -import { Metadata } from "next"; +import { ThemeProvider } from "@/components/theme-provider"; +import { Toaster } from "sonner"; -const poppins = Poppins({ - subsets: ["latin"], - weight: ["100", "200", "300", "400", "500", "600", "700", "800", "900"], - variable: "--font-poppins", -}); - -export const metadata:Metadata = { - title: "NebulaS3 - Advanced AWS S3 Management for Secure Cloud Storage", - description: - "NebulaS3 is a powerful SaaS platform that simplifies AWS S3 bucket management with secure authentication, bucket operations, and cloud storage solutions.", - keywords: [ - "NebulaS3", - "AWS S3 Management", - "Cloud Storage Solutions", - "S3 Bucket Operations", - "AWS STS Authentication", - "Secure Cloud Storage", - "S3 SaaS Platform", - "Manage AWS S3", - ], - openGraph: { - title: "NebulaS3 - Advanced AWS S3 Management for Secure Cloud Storage", - description: - "Manage your AWS S3 buckets with ease using NebulaS3. Secure authentication, bucket creation, and cloud storage solutions tailored for efficiency.", - url: "https://yourdomain.com", - type: "website", - images: [ - { - url: "https://yourdomain.com/images/home-og.jpg", - width: 1200, - height: 630, - alt: "NebulaS3 - AWS S3 Management Platform", - }, - ], - }, +export const metadata: Metadata = { + title: "NebulaS3", + description: "NebulaS3 is a simple and easy-to-use S3-compatible object storage service.", }; export default function RootLayout({ @@ -47,12 +14,21 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - - - {children} - - - + + + + +
+ {children} +
+ +
+ + ); } diff --git a/app/loading.tsx b/app/loading.tsx new file mode 100644 index 0000000..e8c149c --- /dev/null +++ b/app/loading.tsx @@ -0,0 +1,5 @@ +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 old mode 100644 new mode 100755 index 8083acf..c13c3d2 --- a/app/not-found.tsx +++ b/app/not-found.tsx @@ -1,31 +1,37 @@ -import React from 'react' -import Link from "next/link"; -import { ArrowLeft } from 'lucide-react'; -import Footer from '@/components/footer'; -type Props = object; - -export default function NotFound({}: Props) { - return ( -
-
- - 404 - Page Not Found - -
- The requested page couldn't be found. Please check the URL or navigate back home. -
- -
- - - Return Home - -
-
-
-
- ) -} \ No newline at end of file +"use client"; + +import React from "react"; +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("/"); + }; + + return ( +
+
+ +
+ + 404 - Page Not Found + +
+ The requested page couldn't be found. Please check the URL or + navigate back home. +
+ +
+ +
+
+
+
+
+ ); +} diff --git a/app/page.tsx b/app/page.tsx old mode 100644 new mode 100755 index f23e661..1f58811 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,40 +1,19 @@ -import { Book } from "lucide-react"; -import Link from "next/link"; import dynamic from "next/dynamic"; -import { NoticeBanner } from "@/components/notice-banner"; +import BackgroundShape from "@/components/custom-ui/home/BackgroundShape"; +import HomeHero from "@/components/custom-ui/home/HomeHero"; -const DashboardBtn = dynamic(() => import("@/components/home-page/dash-btn")); -const Footer = dynamic(() => import("@/components/footer")); -const Navbar = dynamic(() => import("@/components/home-page/navbar")); -const HealthCheck = dynamic(() => import("@/components/health-check")); -const Logo = dynamic(() => import("@/components/pg-logo")); +const Footer = dynamic(() => import("@/components/layout/Footer")); +const Navbar = dynamic(() => import("@/components/layout/Navbar")); export default function Home() { return ( -
- - -
- -
- S3 Advance SaaS is a modern application that transforms how users - interact with Amazon S3, offering a sophisticated web interface with - enhanced capabilities. -
- -
- - - - Documentation - -
-
+
+ +
+ +
+ {/* */} +
); diff --git a/components.json b/components.json old mode 100644 new mode 100755 index a312865..5a3c750 --- a/components.json +++ b/components.json @@ -4,7 +4,7 @@ "rsc": true, "tsx": true, "tailwind": { - "config": "tailwind.config.ts", + "config": "", "css": "app/globals.css", "baseColor": "zinc", "cssVariables": true, diff --git a/components/DashboardButton.tsx b/components/DashboardButton.tsx new file mode 100755 index 0000000..48a586a --- /dev/null +++ b/components/DashboardButton.tsx @@ -0,0 +1,22 @@ +"use client"; + +import { User } from "lucide-react"; +import Link from "next/link"; +import React from "react"; + +export default function DashboardBtn() { + const [isLogin, setIsLogin] = React.useState(false); + React.useEffect(() => { + setIsLogin(document.cookie.includes("sessionToken")); + }, []); + return ( + + + {isLogin ? "Dashboard" : "Login"} + + ); +} diff --git a/components/HealthCheck.tsx b/components/HealthCheck.tsx new file mode 100755 index 0000000..b5e32ff --- /dev/null +++ b/components/HealthCheck.tsx @@ -0,0 +1,36 @@ +"use client"; +import { cn } from "@/lib/utils"; +import React from "react"; +import LoadingStatus from "./custom-ui/status/LoadingStatus"; +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({ + compact = false, +}: HealthCheckProps) { + const { status } = useHealthCheck(); + + return ( +
+ {getStatusMessage(status)} +
+ ); +} diff --git a/components/Logo.tsx b/components/Logo.tsx new file mode 100755 index 0000000..7137640 --- /dev/null +++ b/components/Logo.tsx @@ -0,0 +1,33 @@ +import { GalleryVerticalEnd } from "lucide-react"; +import Link from "next/link"; +import clsx from "clsx"; + +interface LogoProps { + readonly className?: string; + readonly size?: number; // pixel size for icon box + readonly hideText?: boolean; +} + +export default function Logo({ + className = "", + size = 24, + hideText = false, +}: LogoProps) { + return ( + +
+ +
+ {!hideText && NebulaS3} + + ); +} diff --git a/components/app-sidebar.tsx b/components/app-sidebar.tsx deleted file mode 100644 index 900039b..0000000 --- a/components/app-sidebar.tsx +++ /dev/null @@ -1,83 +0,0 @@ -"use client"; - -import * as React from "react"; -import { BookOpen, GalleryVerticalEnd, PaintBucket } from "lucide-react"; - -import { NavMain } from "@/components/nav-main"; -import { NavUser } from "@/components/nav-user"; -import { TeamSwitcher } from "@/components/team-switcher"; -import { - Sidebar, - SidebarContent, - SidebarFooter, - SidebarHeader, - SidebarRail, -} from "@/components/ui/sidebar"; -import { Bucket } from "@/types/bucket"; -import HealthCheck from "./health-check"; -import useBuckets from "@/hooks/useBuckets"; -import { NavSupport } from "./nav-support"; - -export function AppSidebar(props: React.ComponentProps) { - const { key, ...restProps } = props; - const { buckets, isLoading, isError } = useBuckets(); - - const bucketItems = - buckets?.map((bucket: Bucket) => ({ - title: bucket.bucketName, - url: `/dashboard/buckets/${bucket.bucketName}`, - })) || []; - - const data = { - user: { - name: "User", - email: "user@email.com", - avatar: "/avatar.jpg", - }, - teams: [ - { - name: "NebulaS3", - logo: GalleryVerticalEnd, - plan: "Storage", - }, - ], - navMain: [ - { - title: "Buckets", - loading: isLoading, - error: isError, - url: "/dashboard/buckets", - icon: PaintBucket, - isActive: true, - items: isLoading ? [] : isError ? [] : bucketItems, - }, - ], - support: [ - { - name: "Documentation", - url: "/docs", - icon: BookOpen, - isActive: false, - }, - ], - }; - - return ( - - - - - - - - - - - - - - - ); -} - -export default AppSidebar; diff --git a/components/bucket-component/bucket-card.tsx b/components/bucket-component/bucket-card.tsx deleted file mode 100644 index 39ec4ca..0000000 --- a/components/bucket-component/bucket-card.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Bucket } from "@/types/bucket"; -import Link from "next/link"; -import { InfoMenu } from "./info-menu"; -import { BoxIcon } from "lucide-react"; - -interface Props { - bucket: Bucket; - onBucketDelete: () => void; -} - -const BucketCard = ({ bucket, onBucketDelete }: Props) => { - return ( -
-
- -
-
-
- - - {bucket.region || "N/A"} - - - {bucket.size ?? 0} - -
- -

- {bucket.bucketName} -

- -
-

- Created on{" "} - {new Date(bucket.createdOn).toLocaleDateString("en-US", { - year: "numeric", - month: "long", - day: "numeric", - })} -

-
-
-
- ); -}; - -export default BucketCard; diff --git a/components/bucket-component/create-bkt.tsx b/components/bucket-component/create-bkt.tsx deleted file mode 100644 index ba3bf0a..0000000 --- a/components/bucket-component/create-bkt.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { useState } from "react"; -import { toast } from "sonner"; -import { Button } from "@/components/ui/button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { createBucket } from "@/services/bucketService"; - -interface CreateBucketProps { - onBucketCreated: () => void; -} -export const CreateBucket: React.FC = ({ - onBucketCreated, -}) => { - const [bucketName, setBucketName] = useState(""); - const [isDialogOpen, setIsDialogOpen] = useState(false); - const [isLoading, setIsLoading] = useState(false); - - const validateBucketName = (name: string) => /^[a-zA-Z0-9-]+$/.test(name); - - const handleSubmit = async () => { - if (!validateBucketName(bucketName)) { - toast.error("Invalid bucket name. Use only letters, numbers, and '-'."); - return; - } - - setIsLoading(true); - try { - await createBucket(bucketName); - toast.success("Bucket created successfully!"); - setIsDialogOpen(false); - setBucketName(""); - onBucketCreated(); - } catch (err) { - console.error("Bucket creation error:", err); - } finally { - setIsLoading(false); - } - }; - - return ( - - - - - - - - Create a New Bucket - - - Enter a unique bucket name. - - - - setBucketName(e.target.value)} - className="dark:bg-gray-800 dark:text-white dark:border-gray-700" - /> - - - - - - ); -}; diff --git a/components/bucket-component/filter.tsx b/components/bucket-component/filter.tsx deleted file mode 100644 index 57b7875..0000000 --- a/components/bucket-component/filter.tsx +++ /dev/null @@ -1,41 +0,0 @@ -// components/bucket-component/filter.tsx -import React from "react"; -import { ArrowUpAzIcon, Calendar } from "lucide-react"; - -interface Props { - activeFilter: "size" | "date" | null; - setActiveFilter: (filter: "size" | "date" | null) => void; -} - -const FilterButtons = ({ activeFilter, setActiveFilter }: Props) => { - return ( -
- - -
- ); -}; - -export default FilterButtons; diff --git a/components/bucket-component/info-menu.tsx b/components/bucket-component/info-menu.tsx deleted file mode 100644 index bedf390..0000000 --- a/components/bucket-component/info-menu.tsx +++ /dev/null @@ -1,225 +0,0 @@ -import { Bucket } from "@/types/bucket"; -import { Button } from "@/components/ui/button"; -import { Label } from "@/components/ui/label"; -import { - Sheet, - SheetClose, - SheetContent, - SheetDescription, - SheetFooter, - SheetHeader, - SheetTitle, - SheetTrigger, -} from "@/components/ui/sheet"; -import { Calendar, Info } from "lucide-react"; -import { toast } from "sonner"; -import { deleteBucket } from "@/services/bucketService"; - -interface InfoMenuProps { - bucket: Bucket; - onBucketDelete: () => void; -} - -export function InfoMenu({ bucket, onBucketDelete }: InfoMenuProps) { - const handleDelete = async () => { - try { - const res = await deleteBucket(bucket.bucketName); - onBucketDelete(); - toast.success(res.message); - } catch (err) { - console.log("Error deleting bucket:", err); - } - }; - - return ( - - - - - - - - Bucket Information - - - View and manage your bucket details and settings - - - -
- {/* Basic Info Section */} -
-

- Basic Information -

-
- -
- - {bucket.bucketName} - -
-
-
- - - {bucket.region.toUpperCase() || "N/A"} - -
-
- - - - {bucket.isPublic ? "Public" : "Private"} - - -
-
- - {/* Stats Section */} -
-

- Storage Statistics -

-
-
-
- - - -
-
-

Total Size

-

- {bucket.size || "0 B"} -

-
-
-
-
- - - -
-
-

Files

-

- {bucket.files || 0} -

-
-
-
-
- - - -
-
-

Folders

-

- {bucket.folders || 0} -

-
-
-
-
- - {/* Creation Info */} -
-

- Creation Details -

-
-
- -
-
-

Created On

-

- {new Date(bucket.createdOn).toLocaleDateString("en-US", { - year: "numeric", - month: "long", - day: "numeric", - hour: "2-digit", - minute: "2-digit", - })} -

-
-
-
-
- - - - - - - -
-
- ); -} diff --git a/components/bucket-component/regions.tsx b/components/bucket-component/regions.tsx deleted file mode 100644 index ae8ba66..0000000 --- a/components/bucket-component/regions.tsx +++ /dev/null @@ -1,76 +0,0 @@ -"use client" - -import * as React from "react" - -import { Button } from "@/components/ui/button" -import { - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList, -} from "@/components/ui/command" -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/components/ui/popover" - -type Status = { - value: string - label: string -} - -const statuses: Status[] = [ - { - value: "ap-south-1", - label: "AP-SOUTH-1", - }, -] - -export default function SelectRegions() { - const [open, setOpen] = React.useState(false) - const [selectedStatus, setSelectedStatus] = React.useState( - statuses[0] || null - ) - - return ( -
-

Region

- - - - - - - - - No results found. - - {statuses.map((status) => ( - { - setSelectedStatus( - statuses.find((priority) => priority.value === value) || - null - ) - setOpen(false) - }} - > - {status.label} - - ))} - - - - - -
- ) -} diff --git a/components/custom-ui/SlideNav/SlideNav.tsx b/components/custom-ui/SlideNav/SlideNav.tsx new file mode 100755 index 0000000..8e9dc8c --- /dev/null +++ b/components/custom-ui/SlideNav/SlideNav.tsx @@ -0,0 +1,18 @@ +"use client"; +import SlideNavOverlay from "./SlideNavOverlay"; +import SlideNavPanel from "./SlideNavPanel"; + +export default function SlideNav({ + isOpen, + setIsOpen, +}: { + readonly isOpen: boolean; + readonly setIsOpen: (isOpen: boolean) => void; +}) { + return ( + <> + setIsOpen(false)} /> + + + ); +} diff --git a/components/custom-ui/SlideNav/SlideNavCloseButton.tsx b/components/custom-ui/SlideNav/SlideNavCloseButton.tsx new file mode 100755 index 0000000..a90ec08 --- /dev/null +++ b/components/custom-ui/SlideNav/SlideNavCloseButton.tsx @@ -0,0 +1,18 @@ +"use client"; +import { X } from "lucide-react"; + +export default function SlideNavCloseButton({ + onClick, +}: { + readonly onClick: () => void; +}) { + return ( + + ); +} diff --git a/components/custom-ui/SlideNav/SlideNavLinks.tsx b/components/custom-ui/SlideNav/SlideNavLinks.tsx new file mode 100755 index 0000000..1a49d4f --- /dev/null +++ b/components/custom-ui/SlideNav/SlideNavLinks.tsx @@ -0,0 +1,39 @@ +"use client"; + +export default function SlideNavLinks() { + return ( + + ); +} diff --git a/components/custom-ui/SlideNav/SlideNavOverlay.tsx b/components/custom-ui/SlideNav/SlideNavOverlay.tsx new file mode 100755 index 0000000..de119cc --- /dev/null +++ b/components/custom-ui/SlideNav/SlideNavOverlay.tsx @@ -0,0 +1,22 @@ +"use client"; +import { cn } from "@/lib/utils"; + +export default function SlideNavOverlay({ + isOpen, + onClick, +}: { + readonly isOpen: boolean; + readonly onClick: () => void; +}) { + return ( + +); + +const Input = ({ + className, + ...props +}: React.InputHTMLAttributes) => ( + +); + +const Label = ({ + className, + ...props +}: React.LabelHTMLAttributes) => ( +