From e1ea974b1806e5dd36f1125f84b24294a7820aa7 Mon Sep 17 00:00:00 2001
From: fullsizemalt <106900403+fullsizemalt@users.noreply.github.com>
Date: Fri, 19 Dec 2025 18:24:52 -0800
Subject: [PATCH] feat(ui): Implement AuraUI Bento Grid Dashboard - Added
BentoGrid and BentoCard components for modular widget layout - Refactored
DashboardPage to use the new command center grid - Improved visual hierarchy
with large KPI cards and operational activity feeds - Maintained responsive
layout for mobile and desktop
---
frontend/src/components/aura/BentoGrid.tsx | 129 +++++++++++
frontend/src/pages/DashboardPage.tsx | 239 ++++++++-------------
2 files changed, 216 insertions(+), 152 deletions(-)
create mode 100644 frontend/src/components/aura/BentoGrid.tsx
diff --git a/frontend/src/components/aura/BentoGrid.tsx b/frontend/src/components/aura/BentoGrid.tsx
new file mode 100644
index 0000000..4f31d02
--- /dev/null
+++ b/frontend/src/components/aura/BentoGrid.tsx
@@ -0,0 +1,129 @@
+import { ReactNode } from "react";
+import { ArrowRight, LucideIcon } from "lucide-react";
+import { Link } from "react-router-dom";
+import { cn } from "../../lib/utils";
+
+/**
+ * Bento Grid Components - Feature 14 from AuraUI
+ *
+ * A responsive, modular grid system for dashboard widgets.
+ */
+
+// --- Grid Container ---
+export const BentoGrid = ({
+ children,
+ className,
+}: {
+ children: ReactNode;
+ className?: string;
+}) => {
+ return (
+
+ {children}
+
+ );
+};
+
+// --- Grid Item (Card) ---
+interface BentoCardProps {
+ name: string;
+ className: string;
+ background?: ReactNode;
+ Icon: LucideIcon;
+ description: string;
+ href: string;
+ cta?: string;
+ children?: ReactNode; // For custom content inside the card
+ value?: string | number; // For metric displays
+}
+
+export const BentoCard = ({
+ name,
+ className,
+ background,
+ Icon,
+ description,
+ href,
+ cta = "View Details",
+ children,
+ value
+}: BentoCardProps) => {
+ return (
+
+ {/* Background Graphic (optional) */}
+
+ {background}
+
+
+ {/* Content Area */}
+
+
+ {/* Header */}
+
+
+ {/* Metric Value (if provided) - Large display */}
+ {value && (
+
+
+ {value}
+
+
+ )}
+
+ {/* Description / Subtext */}
+
+ {description}
+
+
+ {/* Custom Children (Charts, Lists, etc) */}
+ {children && (
+
+ {children}
+
+ )}
+
+
+ {/* Interactive Footer / CTA */}
+
+
+ {/* Hover Effect Details */}
+
+
+ );
+};
diff --git a/frontend/src/pages/DashboardPage.tsx b/frontend/src/pages/DashboardPage.tsx
index 511fd6e..fd920d2 100644
--- a/frontend/src/pages/DashboardPage.tsx
+++ b/frontend/src/pages/DashboardPage.tsx
@@ -1,14 +1,13 @@
import { useState, useEffect } from 'react';
-import { Link } from 'react-router-dom';
import { useAuth } from '../context/AuthContext';
import { useToast } from '../context/ToastContext';
-import { Plus, Leaf, ClipboardCheck, AlertTriangle, Activity, Droplet, Salad, Bug, Eye, ArrowRight } from 'lucide-react';
+import { Leaf, ClipboardCheck, Activity, Droplet, Salad, Bug, Eye, CalendarDays, Zap } from 'lucide-react';
import TouchPointModal from '../components/touchpoints/TouchPointModal';
-import TasksDueTodayWidget from '../components/tasks/TasksDueTodayWidget';
import { SmartAlerts } from '../components/dashboard/SmartAlerts';
import { touchPointsApi } from '../lib/touchPointsApi';
import { analyticsApi, AnalyticsOverview } from '../lib/analyticsApi';
import { PullToRefresh } from '../components/ui/PullToRefresh';
+import { BentoGrid, BentoCard } from '../components/aura/BentoGrid';
export default function DashboardPage() {
const { user } = useAuth();
@@ -17,7 +16,6 @@ export default function DashboardPage() {
const [recentActivity, setRecentActivity] = useState([]);
const [overview, setOverview] = useState(null);
const [loading, setLoading] = useState(true);
- const [activityLoading, setActivityLoading] = useState(true);
useEffect(() => {
loadData();
@@ -25,7 +23,6 @@ export default function DashboardPage() {
const loadData = async () => {
setLoading(true);
- setActivityLoading(true);
try {
const [overviewData, activityData] = await Promise.all([
analyticsApi.getOverview(),
@@ -38,7 +35,6 @@ export default function DashboardPage() {
addToast('Failed to load dashboard data', 'error');
} finally {
setLoading(false);
- setActivityLoading(false);
}
};
@@ -47,6 +43,10 @@ export default function DashboardPage() {
addToast('Activity logged successfully', 'success');
};
+ const handlePullRefresh = async () => {
+ await loadData();
+ };
+
const getActivityIcon = (type: string) => {
const iconClass = "transition-transform duration-fast";
switch (type) {
@@ -58,176 +58,111 @@ export default function DashboardPage() {
}
};
- const metrics = overview ? [
- { label: 'Active Batches', value: overview.activeBatches.toString(), icon: Leaf, accent: 'accent', link: '/batches' },
- { label: 'Total Plants', value: overview.totalPlants.toString(), icon: Leaf, accent: 'success', link: '/batches' },
- { label: 'Tasks Completed', value: overview.tasksCompletedThisWeek.toString(), icon: ClipboardCheck, accent: 'accent', link: '/tasks' },
- { label: 'Tasks Pending', value: overview.tasksPending.toString(), icon: AlertTriangle, accent: 'warning', link: '/tasks' },
- { label: 'Touch Points', value: overview.touchPointsToday.toString(), icon: Activity, accent: 'accent', link: '/touch-points' },
- { label: 'Rooms', value: overview.totalRooms.toString(), icon: Leaf, accent: 'neutral', link: '/rooms' },
- ] : [];
-
- const handlePullRefresh = async () => {
- await loadData();
- };
-
- const getAccentClasses = (accent: string) => {
- switch (accent) {
- case 'success': return 'bg-success-muted text-success';
- case 'warning': return 'bg-warning-muted text-warning';
- case 'destructive': return 'bg-destructive-muted text-destructive';
- case 'accent': return 'bg-accent-muted text-accent';
- default: return 'bg-tertiary text-secondary';
- }
- };
-
return (
-
+
{/* Header */}
-
+
-
- Welcome back, {user?.name || user?.email?.split('@')[0]}
+
+ Command Center
-
+
{new Date().toLocaleDateString('en-US', { weekday: 'long', month: 'long', day: 'numeric' })}
-
-
- Online
+
+
- {/* Smart Alerts */}
-
-
- {/* Metrics Grid */}
-
- {loading ? (
- Array.from({ length: 6 }).map((_, i) => (
-
- ))
- ) : (
- metrics.map((m, i) => (
-
-
-
- {m.value}
-
-
- {m.label}
-
-
- ))
- )}
-
-
-
- {/* Tasks Widget */}
-
-
- {/* Recent Activity */}
-
-
-
Recent Activity
-
+
+ {/* 1. Smart Alerts & Overview (Top Left - Large) */}
+ }
+ >
+
+
+
- {activityLoading ? (
-
- {Array.from({ length: 3 }).map((_, i) => (
-
- ))}
-
- ) : recentActivity.length === 0 ? (
-
-
-
No recent activity
-
-
- ) : (
-
- {recentActivity.map((activity: any) => (
+ {/* 2. Active Batches (Top Right - Small) */}
+
}
+ />
+
+ {/* 3. Tasks (Bottom Left - Small) */}
+
}
+ />
+
+ {/* 4. Recent Activity (Bottom Right - Large) */}
+
+
+ {loading ? (
+
+ ) : recentActivity.length === 0 ? (
+
No recent activity logged.
+ ) : (
+ recentActivity.map((activity) => (
-
+
{getActivityIcon(activity.type)}
-
- {activity.type}
- —
+
{activity.batch?.name || 'Unknown Batch'}
+ — {activity.type}
-
- {activity.user?.name || 'User'} · {new Date(activity.createdAt).toLocaleString()}
+
+ {activity.user?.name} · {new Date(activity.createdAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
- {activity.notes && (
-
- "{activity.notes}"
-
- )}
- ))}
-
- )}
-
-
-
- {/* Floating Action Button (Mobile) */}
-
+ ))
+ )}
+
+
+