fix: improve the ui

This commit is contained in:
2026-08-25 11:37:44 +07:00
parent 840aac12d9
commit c773f3d32c
2 changed files with 14 additions and 121 deletions
@@ -3,7 +3,6 @@ import {
FolderTree,
FolderOpen,
RotateCw,
Sparkles,
AlertCircle,
Plus,
DownloadCloud,
@@ -14,7 +13,6 @@ import {
Lock,
ArrowUpDown,
HardDrive,
CheckCircle2,
RefreshCw,
Cable,
} from "lucide-react"
@@ -164,27 +162,28 @@ export function BucketStatsTable({ stats, isLoading, error }: BucketStatsTablePr
</div>
{/* Main Action Buttons */}
<div className="flex items-center gap-2 flex-wrap sm:flex-nowrap">
<div className="flex items-center gap-1.5 sm:gap-2 w-full md:w-auto">
<Button
variant="outline"
size="sm"
onClick={() => setIsImportOpen(true)}
disabled={isLoading}
className="h-8 text-xs px-3 rounded-lg gap-1.5 font-medium shadow-xs"
className="flex-1 md:flex-none h-8 text-[11px] sm:text-xs px-2 sm:px-3 rounded-lg gap-1 sm:gap-1.5 font-medium shadow-xs whitespace-nowrap justify-center min-w-0"
title="Import existing Google Drive folders into your storage root"
>
<DownloadCloud className="h-3.5 w-3.5 text-blue-500" />
<span>Import from Drive</span>
<DownloadCloud className="h-3.5 w-3.5 text-blue-500 shrink-0" />
<span className="hidden sm:inline">Import from Drive</span>
<span className="sm:hidden">Import</span>
</Button>
<Button
size="sm"
onClick={() => setIsCreateOpen(true)}
disabled={isLoading}
className="h-8 text-xs px-3.5 rounded-lg gap-1.5 font-medium shadow-sm shadow-blue-500/20"
className="flex-1 md:flex-none h-8 text-[11px] sm:text-xs px-2 sm:px-3.5 rounded-lg gap-1 sm:gap-1.5 font-medium shadow-sm shadow-blue-500/20 whitespace-nowrap justify-center min-w-0"
title="Create a new storage bucket"
>
<Plus className="h-3.5 w-3.5" />
<Plus className="h-3.5 w-3.5 shrink-0" />
<span>New Bucket</span>
</Button>
@@ -193,11 +192,12 @@ export function BucketStatsTable({ stats, isLoading, error }: BucketStatsTablePr
size="sm"
onClick={handleForceRefresh}
disabled={isLoading || isRefreshing}
className="h-8 text-xs px-2.5 rounded-lg gap-1.5 font-medium shadow-xs"
className="flex-1 md:flex-none h-8 text-[11px] sm:text-xs px-2 sm:px-2.5 rounded-lg gap-1 sm:gap-1.5 font-medium shadow-xs whitespace-nowrap justify-center min-w-0"
title="Recalculate bucket stats from live storage"
>
<RotateCw className={`h-3.5 w-3.5 ${isRefreshing ? "animate-spin text-blue-500" : ""}`} />
<RotateCw className={`h-3.5 w-3.5 shrink-0 ${isRefreshing ? "animate-spin text-blue-500" : ""}`} />
<span className="hidden sm:inline">{isRefreshing ? "Scanning..." : "Recalculate"}</span>
<span className="sm:hidden">{isRefreshing ? "Scanning" : "Recalc"}</span>
</Button>
</div>
</div>
@@ -320,11 +320,11 @@ export function BucketStatsTable({ stats, isLoading, error }: BucketStatsTablePr
</div>
{/* Filter Tabs */}
<div className="flex items-center gap-1.5 self-start sm:self-auto p-1 bg-muted/50 rounded-lg border border-border/60">
<div className="flex items-center justify-between sm:justify-start gap-1.5 w-full sm:w-auto p-1 bg-muted/50 rounded-lg border border-border/60">
<button
type="button"
onClick={() => setFilterTab("all")}
className={`px-2.5 py-1 rounded-md text-xs font-medium transition-all ${
className={`flex-1 sm:flex-initial justify-center px-2.5 py-1 rounded-md text-xs font-medium transition-all flex items-center gap-1 ${
filterTab === "all"
? "bg-card text-foreground shadow-xs font-semibold"
: "text-muted-foreground hover:text-foreground"
@@ -335,7 +335,7 @@ export function BucketStatsTable({ stats, isLoading, error }: BucketStatsTablePr
<button
type="button"
onClick={() => setFilterTab("public")}
className={`px-2.5 py-1 rounded-md text-xs font-medium transition-all flex items-center gap-1 ${
className={`flex-1 sm:flex-initial justify-center px-2.5 py-1 rounded-md text-xs font-medium transition-all flex items-center gap-1 ${
filterTab === "public"
? "bg-card text-emerald-600 dark:text-emerald-400 shadow-xs font-semibold"
: "text-muted-foreground hover:text-foreground"
@@ -347,7 +347,7 @@ export function BucketStatsTable({ stats, isLoading, error }: BucketStatsTablePr
<button
type="button"
onClick={() => setFilterTab("private")}
className={`px-2.5 py-1 rounded-md text-xs font-medium transition-all flex items-center gap-1 ${
className={`flex-1 sm:flex-initial justify-center px-2.5 py-1 rounded-md text-xs font-medium transition-all flex items-center gap-1 ${
filterTab === "private"
? "bg-card text-foreground shadow-xs font-semibold"
: "text-muted-foreground hover:text-foreground"
@@ -661,33 +661,6 @@ export function BucketStatsTable({ stats, isLoading, error }: BucketStatsTablePr
)}
</div>
)}
{/* S3 capabilities bottom bar */}
<div className="pt-3 border-t border-border/60 flex flex-col sm:flex-row sm:items-center justify-between gap-2.5">
<div className="flex flex-wrap items-center gap-1.5">
<span className="text-[11px] font-semibold text-muted-foreground mr-1">S3 API Support:</span>
{[
{ name: "s3:ListObjectsV2", desc: "List bucket objects" },
{ name: "s3:GetObject", desc: "Download objects" },
{ name: "s3:PutObject", desc: "Upload files" },
{ name: "s3:DeleteObject", desc: "Delete files" },
{ name: "s3:CreateMultipartUpload", desc: "Multipart uploads" },
].map((api) => (
<span
key={api.name}
title={api.desc}
className="inline-flex items-center gap-1 px-2.5 py-0.5 rounded-md text-[10px] font-mono font-medium bg-secondary/80 text-secondary-foreground border border-border/60 hover:border-border transition-colors cursor-default"
>
<Sparkles className="h-2.5 w-2.5 text-blue-500" />
{api.name}
</span>
))}
</div>
<div className="text-[11px] text-muted-foreground flex items-center gap-1">
<CheckCircle2 className="h-3 w-3 text-emerald-500" />
<span>Direct Google Drive mapping active</span>
</div>
</div>
</CardContent>
</Card>
-80
View File
@@ -1,30 +1,20 @@
import { useState } from "react"
import { useNavigate } from "react-router"
import { useQueryClient } from "@tanstack/react-query"
import {
Database,
LogOut,
ShieldCheck,
RotateCw,
Radio,
Lock,
Cable,
} from "lucide-react"
import { useAuth } from "@/features/auth/hooks/useAuth"
import { useStatus } from "@/features/status/hooks/useStatus"
import { useBucketStats } from "@/features/status/hooks/useBucketStats"
import { statusKeys } from "@/features/status/api/status.keys"
import { SystemOverviewCard } from "@/features/status/components/SystemOverviewCard"
import { BucketStatsTable } from "@/features/status/components/BucketStatsTable"
import { Button } from "@/components/ui/button"
import { ThemeToggle } from "@/components/ThemeToggle"
import { formatRelativeTime } from "@/lib/format"
export default function Dashboard() {
const { signOut } = useAuth()
const navigate = useNavigate()
const queryClient = useQueryClient()
const [isRefreshing, setIsRefreshing] = useState(false)
const {
data: statusData,
@@ -43,23 +33,6 @@ export default function Dashboard() {
navigate("/sign-in", { replace: true })
}
const handleRefreshAll = async () => {
try {
setIsRefreshing(true)
await queryClient.invalidateQueries({ queryKey: statusKeys.all })
} finally {
setIsRefreshing(false)
}
}
const checkedAtTime = statusData?.checkedAt
? new Date(statusData.checkedAt).toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
})
: null
return (
<div className="relative min-h-screen bg-background bg-grid-pattern flex flex-col overflow-x-hidden">
{/* Navigation Header */}
@@ -95,10 +68,6 @@ export default function Dashboard() {
{/* Actions & Badges */}
<div className="flex items-center gap-2 sm:gap-3 shrink-0">
<div className="hidden md:flex items-center gap-2 px-3 py-1.5 rounded-lg bg-secondary/70 border border-border/60 text-xs text-muted-foreground">
<Lock className="h-3.5 w-3.5 text-emerald-500" />
<span className="font-medium text-foreground/80">Admin Mode</span>
</div>
<button
type="button"
onClick={() => navigate("/integration")}
@@ -124,55 +93,6 @@ export default function Dashboard() {
{/* Main Content */}
<main className="relative z-10 flex-1 max-w-6xl w-full mx-auto p-4 sm:p-6 lg:p-8 space-y-6">
{/* Welcome Banner */}
<div className="relative overflow-hidden rounded-2xl border border-blue-500/20 bg-gradient-to-br from-card via-card to-blue-500/5 p-6 sm:p-7 shadow-lg shadow-blue-950/5">
<div className="absolute top-0 right-0 h-40 w-40 bg-gradient-to-bl from-cyan-500/10 via-blue-500/5 to-transparent rounded-bl-full pointer-events-none" />
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
<div className="space-y-2">
<div className="inline-flex items-center gap-2 px-2.5 py-1 rounded-full bg-emerald-500/10 border border-emerald-500/20 text-emerald-600 dark:text-emerald-400 text-xs font-semibold">
<ShieldCheck className="h-4 w-4" />
<span>Authenticated Session Active</span>
</div>
<h2 className="text-2xl font-bold tracking-tight text-foreground sm:text-3xl">
Gateway Overview &amp; Control
</h2>
<p className="text-sm text-muted-foreground max-w-2xl font-medium">
Live metrics from Cloudflare Workers connected to Google Drive API with AWS S3 signature verification.
</p>
</div>
<div className="flex flex-wrap sm:flex-col items-start sm:items-end gap-2 shrink-0">
<div className="flex items-center gap-2 px-3 py-2 rounded-xl bg-card border border-border/80 shadow-xs">
<Radio className="h-4 w-4 text-emerald-500 animate-pulse" />
<div className="text-left">
<div className="text-[10px] uppercase font-bold text-muted-foreground">Edge Network</div>
<div className="text-xs font-bold text-foreground">Global CDN Active</div>
</div>
</div>
<div className="flex items-center gap-2">
{checkedAtTime && (
<span className="text-[11px] text-muted-foreground font-medium">
Updated {formatRelativeTime(statusData?.checkedAt)} ({checkedAtTime})
</span>
)}
<Button
variant="outline"
size="sm"
onClick={handleRefreshAll}
disabled={isStatusLoading || isBucketsLoading || isRefreshing}
className="h-7 text-xs px-2 rounded-lg gap-1"
title="Refresh status and bucket overview"
>
<RotateCw className={`h-3 w-3 ${isRefreshing ? "animate-spin" : ""}`} />
<span className="hidden min-[420px]:inline">Refresh</span>
</Button>
</div>
</div>
</div>
</div>
{/* Unified Status / Quick Overview */}
<SystemOverviewCard
status={statusData}