From 2d4d7bc61fec1b260c47c00f1fbe630f6e5fe421 Mon Sep 17 00:00:00 2001 From: Vikas Singh <33988028+xanderbilla@users.noreply.github.com> Date: Wed, 11 Jun 2025 23:58:48 +0530 Subject: [PATCH 01/12] xanderbilla:main - Remove api integeration and using mock data --- .gitignore | 2 + app/(main)/buckets/page.tsx | 222 ++++++++++-------- app/api/auth/login/route.ts | 21 -- app/api/health/route.ts | 18 -- app/test/page.tsx | 209 +++++++++++++++++ components/HealthCheck.tsx | 16 +- components/custom-ui/auth/login-form.tsx | 36 ++- .../bucket-component/bucket-filters.tsx | 59 +++-- .../bucket-component/bucket-grid-new.tsx | 139 +++++++++++ .../bucket-component/bucket-grid.tsx | 26 +- .../bucket-component/bucket-header.tsx | 43 +++- .../bucket-component/select-regions.tsx | 149 +++++------- .../custom-ui/dashboard/DashboardOverview.tsx | 75 +++++- .../dashboard/DashboardStatsCards.tsx | 46 +++- components/custom-ui/dashboard/NavActions.tsx | 6 +- components/ui/toast.tsx | 127 ++++++++++ components/ui/toaster.tsx | 35 +++ components/ui/use-toast.tsx | 189 +++++++++++++++ lib/auth.ts | 35 --- lib/hooks/useAuth.ts | 80 ------- lib/hooks/useHealthCheck.ts | 60 ----- middleware.ts | 27 --- package-lock.json | 50 ++++ package.json | 2 + services/authService.ts | 41 ---- services/healthService.ts | 20 -- static/buckets.ts | 51 ++++ types/bucket-filters.ts | 5 +- types/bucket.ts | 15 +- 29 files changed, 1233 insertions(+), 571 deletions(-) delete mode 100644 app/api/auth/login/route.ts delete mode 100644 app/api/health/route.ts create mode 100644 app/test/page.tsx mode change 100755 => 100644 components/custom-ui/bucket-component/bucket-filters.tsx mode change 100755 => 100644 components/custom-ui/bucket-component/bucket-header.tsx mode change 100755 => 100644 components/custom-ui/bucket-component/select-regions.tsx create mode 100644 components/ui/toast.tsx create mode 100644 components/ui/toaster.tsx create mode 100644 components/ui/use-toast.tsx delete mode 100755 lib/auth.ts delete mode 100644 lib/hooks/useAuth.ts delete mode 100644 lib/hooks/useHealthCheck.ts delete mode 100755 middleware.ts delete mode 100755 services/authService.ts delete mode 100755 services/healthService.ts 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/page.tsx b/app/(main)/buckets/page.tsx index e7fdfb1..98330ac 100644 --- a/app/(main)/buckets/page.tsx +++ b/app/(main)/buckets/page.tsx @@ -1,102 +1,120 @@ -"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 ( -
-
- }> - - - - }> - - - - }> - - -
-
- ); -} +"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 + ); + const [selectedRegion, setSelectedRegion] = useState(null); + + useEffect(() => { + setMounted(true); + }, []); + + // Get unique regions from mock data + const availableRegions = Array.from( + new Set( + mockBuckets + .map((bucket) => bucket.region) + .filter((region): region is string => Boolean(region)) + ) + ); + + const filteredBuckets = mockBuckets + .filter((bucket) => + bucket.bucketName.toLowerCase().includes(searchQuery.toLowerCase()) + ) + .filter((bucket) => !selectedRegion || bucket.region === selectedRegion) + .sort((a, b) => { + if (activeFilter === "size") { + const sizeA = parseFloat(a.size ?? "0"); + const sizeB = parseFloat(b.size ?? "0"); + return sizeB - sizeA; + } else if (activeFilter === "date") { + const dateA = + typeof a.createdOn === "string" ? new Date(a.createdOn) : a.createdOn; + const dateB = + typeof b.createdOn === "string" ? new Date(b.createdOn) : b.createdOn; + return dateB.getTime() - dateA.getTime(); + } + return 0; + }); + + const handleBucketDelete = (bucketId: string) => { + // Implement delete functionality + console.log("Delete bucket:", bucketId); + }; + + if (!mounted) { + return ( +
+
+ + + +
+
+ ); + } + + return ( +
+
+ }> + + + + }> + + + + }> + + +
+
+ ); +} 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/test/page.tsx b/app/test/page.tsx new file mode 100644 index 0000000..2f62ef4 --- /dev/null +++ b/app/test/page.tsx @@ -0,0 +1,209 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { Button } from "@/components/ui/button"; +import { toast } from "@/components/ui/use-toast"; +import type { Bucket } from "@/types/bucket"; +import BucketGridNew from "@/components/custom-ui/bucket-component/bucket-grid-new"; +import { mockBuckets } from "@/static/buckets"; + +export default function TestPage() { + const [buckets, setBuckets] = useState([]); + const [selectedBucket, setSelectedBucket] = useState(null); + const [bucketDetails, setBucketDetails] = useState(null); + const [loading, setLoading] = useState(true); + const [detailsLoading, setDetailsLoading] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + // Show welcome toast when component mounts + toast({ + title: "S3 Bucket Test Page", + description: + "This page demonstrates the integration with hardcoded S3 bucket data.", + }); + + fetchBuckets(); + }, []); + + const fetchBuckets = async () => { + setLoading(true); + setError(null); + + try { + // Simulate API call delay + await new Promise((resolve) => setTimeout(resolve, 1000)); + + setBuckets(mockBuckets); + toast({ + title: "Success", + description: `Loaded ${mockBuckets.length} buckets`, + variant: "default", + }); + } catch (err) { + const errorMessage = + err instanceof Error ? err.message : "Failed to fetch buckets"; + setError(errorMessage); + toast({ + title: "Error", + description: errorMessage, + variant: "destructive", + }); + console.error(err); + } finally { + setLoading(false); + } + }; + + const fetchBucketDetails = async (bucketName: string) => { + setDetailsLoading(true); + setError(null); + + try { + // Simulate API call delay + await new Promise((resolve) => setTimeout(resolve, 800)); + + // Find the bucket details from mock data + const bucket = mockBuckets.find((b) => b.bucketName === bucketName); + if (bucket) { + setBucketDetails({ + ...bucket, + numberOfObjects: Math.floor(Math.random() * 1000) + 100, + numberOfFolders: Math.floor(Math.random() * 50) + 10, + versioningEnabled: Math.random() > 0.5, + storageClass: ["STANDARD", "COLD", "GLACIER"][ + Math.floor(Math.random() * 3) + ], + lastUsed: new Date( + Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000 + ), // Random date within last 30 days + }); + setSelectedBucket(bucketName); + toast({ + title: "Bucket Details Loaded", + description: `Details for ${bucketName} retrieved successfully`, + variant: "default", + }); + } else { + throw new Error("Bucket not found"); + } + } catch (err) { + const errorMessage = + err instanceof Error ? err.message : "Failed to fetch bucket details"; + setError(errorMessage); + toast({ + title: "Error", + description: errorMessage, + variant: "destructive", + }); + console.error(err); + setBucketDetails(null); + } finally { + setDetailsLoading(false); + } + }; + + const handleDelete = (id: string) => { + console.log(`Delete bucket with id: ${id}`); + toast({ + title: "Delete Request", + description: `Request to delete bucket with ID: ${id}`, + variant: "default", + }); + }; + + return ( +
+
+ {" "} +
+

S3 Bucket Test Page

+

+ Using hardcoded S3 bucket data for demonstration +

+
+ +
+ + {error && ( +
+ {error} +
+ )} + +
+
+
+

Buckets List (JSON)

+ {loading ? ( +
+

Loading buckets...

+
+ ) : buckets.length === 0 ? ( +
+

No buckets found

+
+ ) : ( +
+
+                  {JSON.stringify(buckets, null, 2)}
+                
+
+ {buckets.map((bucket) => ( + + ))} +
+
+ )} +
+ +
+

+ Bucket Details (JSON) +

+ {!selectedBucket ? ( +
+

Select a bucket to view details

+
+ ) : detailsLoading ? ( +
+

Loading details...

+
+ ) : bucketDetails ? ( +
+                {JSON.stringify(bucketDetails, null, 2)}
+              
+ ) : ( +
+

No details available

+
+ )} +
+
+ +
+

Buckets Grid View

+ +
+
+
+ ); +} diff --git a/components/HealthCheck.tsx b/components/HealthCheck.tsx index b5e32ff..3d42226 100755 --- a/components/HealthCheck.tsx +++ b/components/HealthCheck.tsx @@ -1,36 +1,28 @@ "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({ + className, compact = false, }: HealthCheckProps) { - const { status } = useHealthCheck(); - + // Hardcoded to always show success status return (
- {getStatusMessage(status)} +
); } diff --git a/components/custom-ui/auth/login-form.tsx b/components/custom-ui/auth/login-form.tsx index e2271f3..d1f7b9a 100755 --- a/components/custom-ui/auth/login-form.tsx +++ b/components/custom-ui/auth/login-form.tsx @@ -2,7 +2,17 @@ import { cn } from "@/lib/utils"; import { useState, useEffect } from "react"; -import { useAuth } from "@/lib/hooks/useAuth"; +import { useRouter } from "next/navigation"; + +// Simple toast implementation +const toast = { + success: (message: string) => { + console.log("✅ Success:", message); + }, + error: (message: string) => { + console.error("❌ Error:", message); + }, +}; // Inline minimal components to reduce imports const Button = ({ @@ -103,8 +113,10 @@ export default function LoginForm({ secretKey: "", region: "ap-south-1", }); - const { login, loading, isRedirecting } = useAuth(); + const [loading, setLoading] = useState(false); + const [isRedirecting, setIsRedirecting] = useState(false); const [isClientLoading, setIsClientLoading] = useState(true); + const router = useRouter(); // Simulate initial loading state using useEffect useEffect(() => { @@ -116,7 +128,25 @@ export default function LoginForm({ const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - await login(credentials); + setLoading(true); + + try { + // Hardcoded login - simulate successful authentication + await new Promise((resolve) => setTimeout(resolve, 1500)); // Simulate API call + + // Set hardcoded session tokens + document.cookie = `sessionToken=hardcoded-session-token; path=/; Secure; SameSite=Strict`; + document.cookie = `accessKey=${credentials.accessKey}; path=/; Secure; SameSite=Strict`; + + toast.success("Login successful!"); + setIsRedirecting(true); + router.push("/dashboard"); + } catch (error) { + console.error("Error:", error); + toast.error("Failed to validate credentials"); + } finally { + setLoading(false); + } }; if (isRedirecting) { diff --git a/components/custom-ui/bucket-component/bucket-filters.tsx b/components/custom-ui/bucket-component/bucket-filters.tsx old mode 100755 new mode 100644 index 3a81acc..1e33d10 --- a/components/custom-ui/bucket-component/bucket-filters.tsx +++ b/components/custom-ui/bucket-component/bucket-filters.tsx @@ -1,26 +1,33 @@ -import FilterButtons from "./filter"; -import SearchBar from "./search-bar"; -import SelectRegions from "./select-regions"; -import type { BucketFiltersProps } from "@/types/bucket-filters"; - -export default function BucketFilters({ - searchTerm, - setSearchTerm, - activeFilter, - setActiveFilter, -}: BucketFiltersProps) { - return ( -
-
- -
-
- - -
-
- ); -} +import FilterButtons from "./filter"; +import SearchBar from "./search-bar"; +import SelectRegions from "./select-regions"; +import type { BucketFiltersProps } from "@/types/bucket-filters"; + +export default function BucketFilters({ + searchTerm, + setSearchTerm, + activeFilter, + setActiveFilter, + regions = [], + selectedRegion, + onRegionChange, +}: BucketFiltersProps) { + return ( +
+
+ +
+
+ + +
+
+ ); +} diff --git a/components/custom-ui/bucket-component/bucket-grid-new.tsx b/components/custom-ui/bucket-component/bucket-grid-new.tsx index e69de29..14f724d 100644 --- a/components/custom-ui/bucket-component/bucket-grid-new.tsx +++ b/components/custom-ui/bucket-component/bucket-grid-new.tsx @@ -0,0 +1,139 @@ +"use client"; + +import { useState } from "react"; +import { Eye, Trash2, TrashIcon } from "lucide-react"; +import { BoxIcon } from "lucide-react"; +import ActionMenu, { ActionItem } from "@/components/layout/ActionMenu"; +import ConfirmDialog from "@/components/layout/ConfirmDialog"; +import ItemCard from "@/components/layout/ItemCard"; +import { CardBadge } from "@/types/item-card"; +import type { Bucket } from "@/types/bucket"; + +export interface BucketGridNewProps { + buckets: Bucket[]; + onDelete: (id: string) => void; + onEmpty?: (id: string) => void; + onViewDetails?: (id: string) => void; + loadDetails?: (bucketName: string) => Promise; + isLoading?: boolean; + className?: string; + gridClassName?: string; +} + +export default function BucketGridNew({ + buckets, + onDelete, + onEmpty, + onViewDetails, + loadDetails, + isLoading = false, + className = "", + gridClassName = "", +}: BucketGridNewProps) { + const [bucketToDelete, setBucketToDelete] = useState(null); + const [dialogOpen, setDialogOpen] = useState(false); + + const handleDeleteConfirm = () => { + if (bucketToDelete) { + onDelete(bucketToDelete); + setBucketToDelete(null); + } + }; + + if (isLoading) { + return ( +
+
+ {Array.from({ length: 6 }).map((_, index) => ( +
+ ))} +
+
+ ); + } + + return ( +
+ + +
+ {buckets.map((bucket) => { + // Create a unique ID if not provided + const bucketId = bucket.id || bucket.bucketName; + + // Create badges for region and size + const badges: CardBadge[] = []; + + if (bucket.region) { + badges.push({ label: bucket.region, color: "green" }); + } + + if (bucket.size) { + badges.push({ label: bucket.size, color: "blue" }); + } + + // Build action menu items + const actionItems: ActionItem[] = [ + { + icon: , + label: "View Details", + onClick: () => { + if (loadDetails) { + loadDetails(bucket.bucketName); + } else if (onViewDetails) { + onViewDetails(bucketId); + } + }, + }, + ]; + + if (onEmpty) { + actionItems.push({ + icon: , + label: "Empty Bucket", + onClick: () => onEmpty(bucketId), + }); + } + + actionItems.push({ + icon: , + label: "Delete Bucket", + danger: true, + onClick: () => { + setBucketToDelete(bucketId); + setDialogOpen(true); + }, + }); + + // Convert date string to Date object if needed + const createdDate = typeof bucket.createdOn === 'string' + ? new Date(bucket.createdOn) + : bucket.createdOn; + + return ( + } + badges={badges} + date={createdDate} + actions={} + /> + ); + })} +
+
+ ); +} diff --git a/components/custom-ui/bucket-component/bucket-grid.tsx b/components/custom-ui/bucket-component/bucket-grid.tsx index 34cced7..985b54a 100644 --- a/components/custom-ui/bucket-component/bucket-grid.tsx +++ b/components/custom-ui/bucket-component/bucket-grid.tsx @@ -1,12 +1,32 @@ -import BucketGridRefactored from "./bucket-grid-refactored"; +import BucketGridNew from "./bucket-grid-new"; import type { Bucket } from "@/types/bucket"; interface Props { buckets: Bucket[]; onDelete: (id: string) => void; + onEmpty?: (id: string) => void; + onViewDetails?: (id: string) => void; + loadDetails?: (bucketName: string) => Promise; + isLoading?: boolean; } -export default function BucketGrid({ buckets, onDelete }: Props) { +export default function BucketGrid({ + buckets, + onDelete, + onEmpty, + onViewDetails, + loadDetails, + isLoading +}: Props) { // This is a wrapper around the refactored component to maintain backwards compatibility - return ; + return ( + + ); } diff --git a/components/custom-ui/bucket-component/bucket-header.tsx b/components/custom-ui/bucket-component/bucket-header.tsx old mode 100755 new mode 100644 index 3987eee..e33594f --- a/components/custom-ui/bucket-component/bucket-header.tsx +++ b/components/custom-ui/bucket-component/bucket-header.tsx @@ -1,11 +1,20 @@ -import { Plus } from "lucide-react"; +import { Plus, RefreshCw } from "lucide-react"; import { Button } from "@/components/ui/button"; -export default function BucketHeader() { +interface BucketHeaderProps { + onRefresh?: () => void; + onCreateBucket?: () => void; + isLoading?: boolean; +} + +export default function BucketHeader({ + onRefresh, + onCreateBucket, + isLoading = false +}: BucketHeaderProps = {}) { return (
- {" "}

Buckets

@@ -13,10 +22,28 @@ export default function BucketHeader() { Manage your S3 buckets and their contents

- +
+ {onRefresh && ( + + )} + +
); -} +} \ No newline at end of file diff --git a/components/custom-ui/bucket-component/select-regions.tsx b/components/custom-ui/bucket-component/select-regions.tsx old mode 100755 new mode 100644 index 67cf246..2d5e347 --- a/components/custom-ui/bucket-component/select-regions.tsx +++ b/components/custom-ui/bucket-component/select-regions.tsx @@ -1,92 +1,57 @@ -"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"; - -import type { Status } from "@/types/select-regions"; - -const statuses: Status[] = [ - { - value: "ap-south-1", - label: "AP-SOUTH-1", - }, - { - value: "us-east-1", - label: "US-EAST-1", - }, - { - value: "eu-west-1", - label: "EU-WEST-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} - - ))} - - - -
-
-
-
- ); -} +"use client"; + +import React from "react"; +import { Check, ChevronDown, MapPin } from "lucide-react"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; + +interface SelectRegionsProps { + regions?: string[]; + selectedRegion?: string | null; + onRegionChange?: (region: string | null) => void; +} + +export default function SelectRegions({ + regions = [], + selectedRegion = null, + onRegionChange +}: SelectRegionsProps) { + // If there are no regions or no handler, don't render the component + if (!regions.length || !onRegionChange) { + return null; + } + + return ( + + + + {selectedRegion || "All Regions"} + + + + onRegionChange(null)}> +
+ {!selectedRegion && } + All Regions +
+
+ + {regions.map((region) => ( + onRegionChange(region)} + > +
+ {selectedRegion === region && } + {region} +
+
+ ))} +
+
+ ); +} diff --git a/components/custom-ui/dashboard/DashboardOverview.tsx b/components/custom-ui/dashboard/DashboardOverview.tsx index 5dcfa2a..d257b23 100755 --- a/components/custom-ui/dashboard/DashboardOverview.tsx +++ b/components/custom-ui/dashboard/DashboardOverview.tsx @@ -6,8 +6,38 @@ import { CardTitle, } from "@/components/ui/card"; import React from "react"; +import { mockBuckets } from "@/static/buckets"; export default function DashboardOverview() { + // Create some mock activity data + const recentActivity = [ + { + action: "Created bucket", + bucket: "mobile-app-assets", + time: "2 hours ago", + }, + { + action: "Uploaded 15 files", + bucket: "user-uploads", + time: "5 hours ago", + }, + { + action: "Updated permissions", + bucket: "website-assets", + time: "1 day ago", + }, + { + action: "Deleted 3 objects", + bucket: "my-backup-bucket", + time: "2 days ago", + }, + { + action: "Enabled versioning", + bucket: "data-analytics", + time: "3 days ago", + }, + ]; + return (
@@ -20,8 +50,30 @@ export default function DashboardOverview() { -
- No data available +
+
+ {mockBuckets.slice(0, 6).map((bucket) => ( +
+
+
+ {bucket.bucketName} +
+
+ {bucket.region} +
+
+
+
{bucket.size}
+
+ {bucket.numberOfObjects} objects +
+
+
+ ))} +
@@ -32,8 +84,23 @@ export default function DashboardOverview() { -
- No recent activity +
+ {recentActivity.map((activity, index) => ( +
+
+ {activity.action} + + {activity.time} + +
+ + Bucket: {activity.bucket} + +
+ ))}
diff --git a/components/custom-ui/dashboard/DashboardStatsCards.tsx b/components/custom-ui/dashboard/DashboardStatsCards.tsx index e13b3c7..2f55034 100755 --- a/components/custom-ui/dashboard/DashboardStatsCards.tsx +++ b/components/custom-ui/dashboard/DashboardStatsCards.tsx @@ -1,7 +1,29 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import React from "react"; +import { mockBuckets } from "@/static/buckets"; export default function DashboardStatsCards() { + // Calculate stats from mock data + const totalBuckets = mockBuckets.length; + const totalObjects = mockBuckets.reduce( + (sum, bucket) => sum + (bucket.numberOfObjects ?? 0), + 0 + ); + const totalStorageGB = mockBuckets.reduce((sum, bucket) => { + const sizeStr = bucket.size ?? "0 GB"; + const regex = /([0-9.]+)\s*(GB|MB)/; + const sizeMatch = regex.exec(sizeStr); + if (sizeMatch) { + const value = parseFloat(sizeMatch[1]); + const unit = sizeMatch[2]; + return sum + (unit === "GB" ? value : value / 1024); + } + return sum; + }, 0); + const activeRegions = Array.from( + new Set(mockBuckets.map((bucket) => bucket.region).filter(Boolean)) + ).length; + return (
@@ -11,9 +33,11 @@ export default function DashboardStatsCards() { -
0
+
+ {totalBuckets} +

- +0% from last month + +20% from last month

@@ -24,9 +48,11 @@ export default function DashboardStatsCards() { -
0
+
+ {totalObjects !== 0 ? totalObjects : 1245} +

- +0% from last month + +15% from last month

@@ -37,9 +63,11 @@ export default function DashboardStatsCards() { -
0 MB
+
+ {totalStorageGB.toFixed(1)} GB +

- +0% from last month + +8% from last month

@@ -50,9 +78,11 @@ export default function DashboardStatsCards() { -
1
+
+ {activeRegions} +

- ap-south-1 + us-east-1, eu-west-1, ap-south-1

diff --git a/components/custom-ui/dashboard/NavActions.tsx b/components/custom-ui/dashboard/NavActions.tsx index f7827ac..98e2389 100755 --- a/components/custom-ui/dashboard/NavActions.tsx +++ b/components/custom-ui/dashboard/NavActions.tsx @@ -2,7 +2,6 @@ import { Menu } from "lucide-react"; import { ModeToggle } from "@/components/theme-mode/ModeToggle"; -import HealthCheck from "@/components/HealthCheck"; import CustomButton from "@/components/ui/custom-button"; import LogoutIcon from "@/components/icons/LogoutIcon"; import { useState, useEffect } from "react"; @@ -36,10 +35,7 @@ export default function NavActions({ onLogout, onMenuClick }: NavActionsProps) { return (
{" "} - + } />{" "}
diff --git a/app/test/page.tsx b/app/test/page.tsx deleted file mode 100644 index 2f62ef4..0000000 --- a/app/test/page.tsx +++ /dev/null @@ -1,209 +0,0 @@ -"use client"; - -import { useState, useEffect } from "react"; -import { Button } from "@/components/ui/button"; -import { toast } from "@/components/ui/use-toast"; -import type { Bucket } from "@/types/bucket"; -import BucketGridNew from "@/components/custom-ui/bucket-component/bucket-grid-new"; -import { mockBuckets } from "@/static/buckets"; - -export default function TestPage() { - const [buckets, setBuckets] = useState([]); - const [selectedBucket, setSelectedBucket] = useState(null); - const [bucketDetails, setBucketDetails] = useState(null); - const [loading, setLoading] = useState(true); - const [detailsLoading, setDetailsLoading] = useState(false); - const [error, setError] = useState(null); - - useEffect(() => { - // Show welcome toast when component mounts - toast({ - title: "S3 Bucket Test Page", - description: - "This page demonstrates the integration with hardcoded S3 bucket data.", - }); - - fetchBuckets(); - }, []); - - const fetchBuckets = async () => { - setLoading(true); - setError(null); - - try { - // Simulate API call delay - await new Promise((resolve) => setTimeout(resolve, 1000)); - - setBuckets(mockBuckets); - toast({ - title: "Success", - description: `Loaded ${mockBuckets.length} buckets`, - variant: "default", - }); - } catch (err) { - const errorMessage = - err instanceof Error ? err.message : "Failed to fetch buckets"; - setError(errorMessage); - toast({ - title: "Error", - description: errorMessage, - variant: "destructive", - }); - console.error(err); - } finally { - setLoading(false); - } - }; - - const fetchBucketDetails = async (bucketName: string) => { - setDetailsLoading(true); - setError(null); - - try { - // Simulate API call delay - await new Promise((resolve) => setTimeout(resolve, 800)); - - // Find the bucket details from mock data - const bucket = mockBuckets.find((b) => b.bucketName === bucketName); - if (bucket) { - setBucketDetails({ - ...bucket, - numberOfObjects: Math.floor(Math.random() * 1000) + 100, - numberOfFolders: Math.floor(Math.random() * 50) + 10, - versioningEnabled: Math.random() > 0.5, - storageClass: ["STANDARD", "COLD", "GLACIER"][ - Math.floor(Math.random() * 3) - ], - lastUsed: new Date( - Date.now() - Math.random() * 30 * 24 * 60 * 60 * 1000 - ), // Random date within last 30 days - }); - setSelectedBucket(bucketName); - toast({ - title: "Bucket Details Loaded", - description: `Details for ${bucketName} retrieved successfully`, - variant: "default", - }); - } else { - throw new Error("Bucket not found"); - } - } catch (err) { - const errorMessage = - err instanceof Error ? err.message : "Failed to fetch bucket details"; - setError(errorMessage); - toast({ - title: "Error", - description: errorMessage, - variant: "destructive", - }); - console.error(err); - setBucketDetails(null); - } finally { - setDetailsLoading(false); - } - }; - - const handleDelete = (id: string) => { - console.log(`Delete bucket with id: ${id}`); - toast({ - title: "Delete Request", - description: `Request to delete bucket with ID: ${id}`, - variant: "default", - }); - }; - - return ( -
-
- {" "} -
-

S3 Bucket Test Page

-

- Using hardcoded S3 bucket data for demonstration -

-
- -
- - {error && ( -
- {error} -
- )} - -
-
-
-

Buckets List (JSON)

- {loading ? ( -
-

Loading buckets...

-
- ) : buckets.length === 0 ? ( -
-

No buckets found

-
- ) : ( -
-
-                  {JSON.stringify(buckets, null, 2)}
-                
-
- {buckets.map((bucket) => ( - - ))} -
-
- )} -
- -
-

- Bucket Details (JSON) -

- {!selectedBucket ? ( -
-

Select a bucket to view details

-
- ) : detailsLoading ? ( -
-

Loading details...

-
- ) : bucketDetails ? ( -
-                {JSON.stringify(bucketDetails, null, 2)}
-              
- ) : ( -
-

No details available

-
- )} -
-
- -
-

Buckets Grid View

- -
-
-
- ); -} diff --git a/components/HealthCheck.tsx b/components/HealthCheck.tsx old mode 100755 new mode 100644 index 3d42226..e997235 --- a/components/HealthCheck.tsx +++ b/components/HealthCheck.tsx @@ -1,7 +1,10 @@ "use client"; + import { cn } from "@/lib/utils"; import React from "react"; +import { useHealthCheck } from "@/hooks/useHealthCheck"; import SuccessStatus from "./custom-ui/status/SuccessStatus"; +import ErrorStatus from "./custom-ui/status/ErrorStatus"; interface HealthCheckProps { readonly className?: string; @@ -12,7 +15,41 @@ export default function HealthCheck({ className, compact = false, }: HealthCheckProps) { - // Hardcoded to always show success status + const { data: health, isLoading, error } = useHealthCheck(); + + if (isLoading) { + return ( +
+
+
+ Checking status... +
+
+ ); + } + + if (error || !health) { + return ( +
+ +
+ ); + } + return (
+ new QueryClient({ + defaultOptions: { + queries: { + staleTime: 5 * 60 * 1000, // 5 minutes + gcTime: 10 * 60 * 1000, // 10 minutes (formerly cacheTime) + 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 < 3; + }, + refetchOnWindowFocus: false, + refetchOnReconnect: 'always', + }, + 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 d1f7b9a..a15ffc7 --- a/components/custom-ui/auth/login-form.tsx +++ b/components/custom-ui/auth/login-form.tsx @@ -1,8 +1,11 @@ "use client"; import { cn } from "@/lib/utils"; -import { useState, useEffect } from "react"; +import { useState } from "react"; import { useRouter } from "next/navigation"; +import { useLogin } from "@/hooks/useAuth"; +import type { LoginCredentials } from "@/types/auth"; +import { ButtonSpinner } from "@/components/ui/spinner"; // Simple toast implementation const toast = { @@ -56,125 +59,47 @@ const Input = ({ const Label = ({ className, + htmlFor, ...props -}: React.LabelHTMLAttributes) => ( -