diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index d8e2a94c..86a27a35 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -23,7 +23,7 @@ import ShareRoutine from "./pages/ShareRoutine.jsx"; import DailyJournal from "./pages/DailyJournal.jsx"; const AuthLayout = ({ children }) => ( -
+
{children}
); @@ -38,9 +38,9 @@ const AnimatedRoutes = () => { path="/" element={ - - - + + + } /> @@ -49,7 +49,9 @@ const AnimatedRoutes = () => { element={ - + + + } @@ -59,7 +61,9 @@ const AnimatedRoutes = () => { element={ - + + + } @@ -77,9 +81,7 @@ const AnimatedRoutes = () => { element={ - - - + } @@ -89,9 +91,7 @@ const AnimatedRoutes = () => { element={ - - - + } @@ -101,9 +101,17 @@ const AnimatedRoutes = () => { element={ - - - + + + + } + /> + + + } @@ -112,9 +120,7 @@ const AnimatedRoutes = () => { path="/profile" element={ - - - + } /> @@ -122,9 +128,7 @@ const AnimatedRoutes = () => { path="/analytics" element={ - - - + } /> @@ -271,4 +275,4 @@ const App = () => { ); }; -export default App; +export default App; \ No newline at end of file diff --git a/frontend/src/components/Dashboard/ContributionHeatmap.jsx b/frontend/src/components/Dashboard/ContributionHeatmap.jsx index d6f23ffb..9e0d659e 100644 --- a/frontend/src/components/Dashboard/ContributionHeatmap.jsx +++ b/frontend/src/components/Dashboard/ContributionHeatmap.jsx @@ -1,5 +1,4 @@ import { useState, useMemo, useRef, useCallback } from "react"; -// eslint-disable-next-line no-unused-vars import { motion, AnimatePresence } from "framer-motion"; import { Flame, Trophy, CheckSquare, Percent, HelpCircle, Activity } from "lucide-react"; import { @@ -15,25 +14,20 @@ export default function ContributionHeatmap({ tasks = [], routineTasks = [] }) { const gridContainerRef = useRef(null); const cardRef = useRef(null); - // Compute real contribution data dynamically from live props const data = useMemo(() => { return generateRealYearlyData(tasks, routineTasks); }, [tasks, routineTasks]); - // Memoize activity stats to prevent redundant calculations on rerender const stats = useMemo(() => calculateHeatmapStats(data), [data]); - // Compute month labels and their column indices dynamically const monthLabels = useMemo(() => { const labels = []; - // 53 columns total (weeks) for (let w = 0; w < 53; w++) { const dayIndex = w * 7; if (dayIndex < data.length) { const date = data[dayIndex].date; const monthName = date.toLocaleDateString("en-US", { month: "short" }); - // If first column or month changes compared to previous week column if (w === 0 || (w > 0 && data[(w - 1) * 7].date.getMonth() !== date.getMonth())) { labels.push({ name: monthName, @@ -45,7 +39,6 @@ export default function ContributionHeatmap({ tasks = [], routineTasks = [] }) { return labels; }, [data]); - // Handle cell hover and focus (for accessibility) const handleInteractionStart = useCallback((e, day) => { const cell = e.currentTarget; if (!cardRef.current) return; @@ -54,7 +47,7 @@ export default function ContributionHeatmap({ tasks = [], routineTasks = [] }) { const cardRect = cardRef.current.getBoundingClientRect(); const x = cellRect.left - cardRect.left + cellRect.width / 2; - const y = cellRect.top - cardRect.top; + const y = cellRect.top - cardRef.current.scrollTop - cardRect.top; setHoveredDay(day); setTooltipPos({ x, y }); @@ -64,7 +57,6 @@ export default function ContributionHeatmap({ tasks = [], routineTasks = [] }) { setHoveredDay(null); }, []); - // Format date for tooltip display const formatFullDate = (date) => { return date.toLocaleDateString("en-US", { weekday: "long", @@ -74,29 +66,23 @@ export default function ContributionHeatmap({ tasks = [], routineTasks = [] }) { }); }; - // Staggered column (week) animation variants const containerVariants = { hidden: { opacity: 0 }, show: { opacity: 1, transition: { - staggerChildren: 0.02, + staggerChildren: 0.002, }, }, }; const columnVariants = { - hidden: { opacity: 0, y: 10 }, - show: { opacity: 1, y: 0, transition: { duration: 0.3, ease: "easeOut" } }, + hidden: { opacity: 0, scale: 0.92 }, + show: { opacity: 1, scale: 1, transition: { duration: 0.2, ease: "easeOut" } }, }; return ( -
- - {/* Background radial soft light for aesthetic premium look */} -
- - {/* Tooltip Popup (Dynamic Absolute Positioning relative to Card) */} +
{hoveredDay && (() => { const colIdx = hoveredDay.colIdx ?? 26; @@ -118,62 +104,59 @@ export default function ContributionHeatmap({ tasks = [], routineTasks = [] }) { top: `${tooltipPos.y}px`, transform: `translate(${translateX}, -100%)`, }} - className="absolute z-50 pointer-events-none mt-[-10px]" + className="absolute z-50 pointer-events-none mt-[-8px]" >
-
- - {formatFullDate(hoveredDay.date)} - +
+ {formatFullDate(hoveredDay.date)}
-
-
- Tasks Done: - +
+
+ Tasks Crushed: + {hoveredDay.tasksCompleted} / {hoveredDay.tasksTotal}
-
- Routines Completed: - +
+ Routines Run: + {hoveredDay.routinesCompleted}
-
- Productivity: +
+ Intensity: - {getProductivityColorDetails(hoveredDay.score).label.split(" (")[0]} + {hoveredDay.score === 3 ? "Perfect" : hoveredDay.score === 2 ? "High" : hoveredDay.score === 1 ? "Moderate" : "None"}
-
+
{hoveredDay.score === 3 ? ( - - 🔥 Perfect productivity streak active! + + 🔥 Perfect deep-work window matching target bounds! ) : hoveredDay.score > 0 ? ( - - ⚡ Keep it going! + + ⚡ Active baseline tracking verified. ) : ( - No active completions + No matching sync points completed )}
- {/* Tooltip pointer arrow */}
- {/* Header and Controls */} -
+
- - + + -

+

Productivity Contribution

- + Live Tracker
-

- Tracking real routines & task completions from your live daily workflow. +

+ Tracking real routines & task completions from your live daily workflow grid.

- {/* Statistics Cards */} -
- - {/* Current Streak */} -
-
- +
+
+
+
-

Current Streak

-

- {stats.currentStreak} days +

Current Streak

+

+ {stats.currentStreak} days

-

Keep the fire burning!

+

Keep the fire burning!

- {/* Longest Streak */} -
-
- +
+
+
-

Longest Streak

-

- {stats.longestStreak} days +

Longest Streak

+

+ {stats.longestStreak} days

-

Your peak productivity

+

Your peak productivity

- {/* Total Productive Days */} -
-
+
+
-

Productive Days

-

- {stats.totalProductiveDays} days +

Productive Days

+

+ {stats.totalProductiveDays} days

-

Days with active completions

+

Days with active completions

- {/* Yearly Productivity Average */} -
-
- +
+
+
-

Day-wise Completion

-

+

Daily Completion

+

{stats.yearlyPercentage}%

-

Average daily task completion rate

+

Average task completion rate

- {/* Main Heatmap Container */} -
- - {/* Loaded Heatmap Display */} -
- {/* Inner Wrapper containing relative position for absolute elements */} -
- - {/* Grid Header Month Labels */} -
-
{/* spacing for weekday column */} -
- {monthLabels.map((lbl, idx) => ( - - {lbl.name} - - ))} -
-
- - {/* Grid Body: Weekday Labels + 53x7 Calendar Squares */} -
- - {/* Weekday Column */} -
- - Mon - - Wed - - Fri - -
+
+
+
+
+
+ {monthLabels.map((lbl, idx) => ( + + {lbl.name} + + ))} +
+
- {/* Calendar Column Flow Grid */} +
+
+ + Mon + + Wed + + Fri + +
+ + + {Array.from({ length: 53 }).map((_, colIdx) => ( - {/* Outer weeks mapped to Framer Motion columns for smooth sweep animation */} - {Array.from({ length: 53 }).map((_, colIdx) => ( - - {Array.from({ length: 7 }).map((_, rowIdx) => { - const dayIdx = colIdx * 7 + rowIdx; - const day = data[dayIdx]; - if (!day) return null; - - if (day.isFuture) { - return ( -
- ); - } - - const colorDetails = getProductivityColorDetails(day.score); - const isPerfect = day.score === 3; - - // Check legend hover filter conditions - const isDimmed = activeLegendScore !== null && day.score !== activeLegendScore; - const isHighlighted = activeLegendScore !== null && day.score === activeLegendScore; - const isSelected = hoveredDay?.dateStr === day.dateStr; - - return ( -
handleInteractionStart(e, day)} - onFocus={(e) => handleInteractionStart(e, day)} - onMouseLeave={handleInteractionEnd} - onBlur={handleInteractionEnd} - onKeyDown={(e) => { - if (e.key === "Escape") handleInteractionEnd(); - }} - className={` - w-[10px] h-[10px] sm:w-[11px] sm:h-[11px] md:w-[13px] md:h-[13px] rounded-[3px] cursor-pointer outline-none relative transition-all duration-300 - border border-transparent - ${colorDetails.bgClass} - ${isPerfect ? colorDetails.glowClass : ""} - ${isDimmed ? "opacity-20 scale-90" : "opacity-100"} - ${isHighlighted ? "scale-115 z-10 !border-slate-500 dark:!border-white shadow-lg" : ""} - ${isSelected ? "scale-120 z-20 !border-slate-600 dark:!border-slate-200 shadow-xl" : ""} - hover:scale-120 hover:z-20 hover:!border-slate-600 dark:hover:!border-slate-200 hover:shadow-xl - focus:ring-2 focus:ring-[#14b8a6] focus:ring-offset-2 focus:ring-offset-white dark:focus:ring-offset-slate-900 focus:scale-115 focus:z-10 - `} - /> - ); - })} - - ))} + {Array.from({ length: 7 }).map((_, rowIdx) => { + const dayIdx = colIdx * 7 + rowIdx; + const day = data[dayIdx]; + if (!day) return null; + + if (day.isFuture) { + return ( +
+ ); + } + + const isPerfect = day.score === 3; + const dayScore = day.score ?? 0; + + const isDimmed = activeLegendScore !== null && dayScore !== activeLegendScore; + const isHighlighted = activeLegendScore !== null && dayScore === activeLegendScore; + const isSelected = hoveredDay?.dateStr === day.dateStr; + + let lightBg = "bg-slate-100"; + if (dayScore === 1) lightBg = "bg-[#3b8ea0]/15"; + if (dayScore === 2) lightBg = "bg-[#3b8ea0]/50"; + if (dayScore === 3) lightBg = "bg-[#4eb7b3]"; + + let darkBg = "dark:bg-slate-800"; + if (dayScore === 1) darkBg = "dark:bg-slate-800/40 dark:border dark:border-slate-800"; + if (dayScore === 2) darkBg = "dark:bg-[#3b8ea0]/40"; + if (dayScore === 3) darkBg = "dark:bg-[#4eb7b3]"; + + return ( +
handleInteractionStart(e, day)} + onFocus={(e) => handleInteractionStart(e, day)} + onMouseLeave={handleInteractionEnd} + onBlur={handleInteractionEnd} + onKeyDown={(e) => { + if (e.key === "Escape") handleInteractionEnd(); + }} + className={` + w-[10px] h-[10px] sm:w-[11px] sm:h-[11px] md:w-[13px] md:h-[13px] rounded-[3px] cursor-pointer outline-none relative transition-all duration-200 + ${lightBg} ${darkBg} + ${isDimmed ? "opacity-15 scale-90" : "opacity-100"} + ${isHighlighted ? "scale-115 z-10 ring-1 ring-slate-400 dark:ring-white shadow-xs" : ""} + ${isSelected ? "scale-115 z-20 ring-2 ring-[#3b8ea0] dark:ring-white shadow-md" : ""} + hover:scale-115 hover:z-20 hover:ring-2 hover:ring-[#3b8ea0] dark:hover:ring-white hover:shadow-xs + focus:ring-2 focus:ring-[#3b8ea0] focus:ring-offset-2 focus:ring-offset-white dark:focus:ring-offset-slate-900 + `} + /> + ); + })} -
- -
+ ))} +
+
- {/* Grid Legend & Instructions */} -
-
- - Hover / Focus cells for details. Hover legend intensities to filter. +
+
+ + Hover cells for full summaries. Filter matching scales using the intensity indicators right.
- {/* Interactive Legend Scale */} -
- Less +
+ Less
{[0, 1, 2, 3].map((score) => { - const details = getProductivityColorDetails(score); - const isPerfect = score === 3; + const dayScore = score; + let lightBg = "bg-slate-100"; + if (score === 1) lightBg = "bg-[#3b8ea0]/15"; + if (score === 2) lightBg = "bg-[#3b8ea0]/50"; + if (score === 3) lightBg = "bg-[#4eb7b3]"; + + let darkBg = "dark:bg-slate-800"; + if (score === 1) darkBg = "dark:bg-slate-800/40"; + if (score === 2) darkBg = "dark:bg-[#3b8ea0]/40"; + if (score === 3) darkBg = "dark:bg-[#4eb7b3]"; + return (
- More + More
- {/* Styled inline components to handle premium visual requirements like custom glows */}
); -} +} \ No newline at end of file diff --git a/frontend/src/components/Dashboard/DashboardTasks.jsx b/frontend/src/components/Dashboard/DashboardTasks.jsx index 358cb994..b4e27f54 100644 --- a/frontend/src/components/Dashboard/DashboardTasks.jsx +++ b/frontend/src/components/Dashboard/DashboardTasks.jsx @@ -2,7 +2,6 @@ import { useNavigate } from "react-router-dom"; import { Plus, ArrowRight, CheckCircle2 } from "lucide-react"; import { useState } from "react"; - export default function DashboardTasks({ tasks, updateTask }) { const navigate = useNavigate(); const [durationModalTask, setDurationModalTask] = useState(null); @@ -15,15 +14,15 @@ export default function DashboardTasks({ tasks, updateTask }) { }; const priorityBorder = { - Low: "border-green-400", - Medium: "border-yellow-400", - High: "border-red-500", + Low: "border-emerald-400 dark:border-emerald-500", + Medium: "border-amber-400 dark:border-amber-500", + High: "border-rose-500", }; const priorityBadge = { - Low: "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400", - Medium: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400", - High: "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400", + Low: "bg-emerald-50 text-emerald-700 dark:bg-emerald-950/30 dark:text-emerald-400", + Medium: "bg-amber-50 text-amber-700 dark:bg-amber-950/30 dark:text-amber-400", + High: "bg-rose-50 text-rose-700 dark:bg-rose-950/30 dark:text-rose-400", }; const today = new Date(); @@ -75,19 +74,18 @@ export default function DashboardTasks({ tasks, updateTask }) { }; return ( -
- {/* Header */} +
-

Today’s Focus

-

Top priorities for today

+

Today’s Focus

+

Top priorities for today

@@ -98,23 +96,21 @@ export default function DashboardTasks({ tasks, updateTask }) { key={task._id} className={`group relative flex items-center gap-4 border-l-4 rounded-xl p-4 transition-all duration-200 ${priorityBorder[task.priority]} - bg-white/80 hover:bg-white dark:bg-slate-800/80 dark:hover:bg-slate-800 shadow-sm hover:shadow-md`} + bg-slate-50 hover:bg-slate-100/70 dark:bg-slate-800/40 dark:hover:bg-slate-800/80 border-y border-r border-slate-200/60 dark:border-slate-800 shadow-xs`} > - {/* Checkbox */} handleCheckboxChange(task)} /> - {/* Task content */}

{task.title} @@ -122,7 +118,7 @@ export default function DashboardTasks({ tasks, updateTask }) {

@@ -130,24 +126,23 @@ export default function DashboardTasks({ tasks, updateTask }) { {task.status === "Completed" && ( - Completed + Completed )}
- {/* Hover affordance */} - +
))}
) : ( -
+
No tasks for today. @@ -196,4 +191,4 @@ export default function DashboardTasks({ tasks, updateTask }) { )}
); -} +} \ No newline at end of file diff --git a/frontend/src/components/Dashboard/InsightCard.jsx b/frontend/src/components/Dashboard/InsightCard.jsx index fdaa0d00..cba684bd 100644 --- a/frontend/src/components/Dashboard/InsightCard.jsx +++ b/frontend/src/components/Dashboard/InsightCard.jsx @@ -1,15 +1,23 @@ export default function InsightCard({ insights }) { return ( -
-

Insights

-
    - {insights?.map((insight, i) => ( -
  • - {insight.icon} - {insight.message} -
  • - ))} -
+
+

Insights

+ {(!insights || insights.length === 0) ? ( +

+ No automated baseline insights compiled for this scheduling window yet. +

+ ) : ( +
    + {insights.map((insight, i) => ( +
  • + + {insight.icon} + + {insight.message} +
  • + ))} +
+ )}
); -} +} \ No newline at end of file diff --git a/frontend/src/components/Dashboard/LiveClock.jsx b/frontend/src/components/Dashboard/LiveClock.jsx index 21168dc4..c686b907 100644 --- a/frontend/src/components/Dashboard/LiveClock.jsx +++ b/frontend/src/components/Dashboard/LiveClock.jsx @@ -24,7 +24,7 @@ const LiveClock = () => { }, []); return ( -

+

{currentTime}

); diff --git a/frontend/src/components/Dashboard/ReflectionSummary.jsx b/frontend/src/components/Dashboard/ReflectionSummary.jsx index 1c9e38d7..b389d033 100644 --- a/frontend/src/components/Dashboard/ReflectionSummary.jsx +++ b/frontend/src/components/Dashboard/ReflectionSummary.jsx @@ -20,25 +20,22 @@ export default function ReflectionSummary({ return (
- {/* Daily Completion */} -
-

Daily Completion

-

{completedToday} / {totalToday}

-

Tasks done today{totalToday > 0 ? ` — ${completionRate}%` : ""}

+
+

Daily Completion

+

{completedToday} / {totalToday}

+

Tasks done today{totalToday > 0 ? ` — ${completionRate}%` : ""}

- {/* Weekly Momentum */} -
-

Weekly Momentum

-

{weeklyCompletionPercent}%

-

{weeklyText}

+
+

Weekly Momentum

+

{weeklyCompletionPercent}%

+

{weeklyText}

- {/* Productivity Insight */} -
-

Productivity Insight

-

{insightText}

-

+

+

Productivity Insight

+

{insightText}

+

{upcomingCount > 0 ? `Next: ${upcomingCount} upcoming task${upcomingCount > 1 ? "s" : ""}` : "No upcoming tasks"} {" · "} {tasks?.length ? `${tasks.length} total` : "0 total"} @@ -47,4 +44,4 @@ export default function ReflectionSummary({

); -} +} \ No newline at end of file diff --git a/frontend/src/components/Dashboard/StatCard.jsx b/frontend/src/components/Dashboard/StatCard.jsx index fa8a6982..5788954c 100644 --- a/frontend/src/components/Dashboard/StatCard.jsx +++ b/frontend/src/components/Dashboard/StatCard.jsx @@ -1,13 +1,15 @@ export default function StatCard({ label, value, subtitle, icon }) { return ( -
-
{icon}
+
+
+ {icon} +
-

{label}

-

{value}

- {subtitle &&

{subtitle}

} +

{label}

+

{value}

+ {subtitle &&

{subtitle}

}
); -} +} \ No newline at end of file diff --git a/frontend/src/components/Dashboard/TaskPreview.jsx b/frontend/src/components/Dashboard/TaskPreview.jsx index d6fff092..5302243e 100644 --- a/frontend/src/components/Dashboard/TaskPreview.jsx +++ b/frontend/src/components/Dashboard/TaskPreview.jsx @@ -2,12 +2,10 @@ import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { ArrowRight } from "lucide-react"; - -export default function TaskPreview({ tasks , updateTask}) { +const TaskPreview = ({ tasks, updateTask }) => { const navigate = useNavigate(); const [durationModalTask, setDurationModalTask] = useState(null); const [actualDuration, setActualDuration] = useState(""); - const [now, setNow] = useState(new Date()); useEffect(() => { @@ -19,15 +17,15 @@ export default function TaskPreview({ tasks , updateTask}) { }, []); const priorityBorder = { - Low: "border-green-400", - Medium: "border-yellow-400", - High: "border-red-500", + Low: "border-emerald-400 dark:border-emerald-500", + Medium: "border-amber-400 dark:border-amber-500", + High: "border-rose-500", }; const priorityBadge = { - Low: "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400", - Medium: "bg-yellow-100 text-yellow-700 dark:bg-yellow-900/30 dark:text-yellow-400", - High: "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400", + Low: "bg-emerald-50 text-emerald-700 dark:bg-emerald-950/30 dark:text-emerald-400", + Medium: "bg-amber-50 text-amber-700 dark:bg-amber-950/30 dark:text-amber-400", + High: "bg-rose-50 text-rose-700 dark:bg-rose-950/30 dark:text-rose-400", }; const handleCheckboxChange = async (task) => { @@ -68,134 +66,119 @@ export default function TaskPreview({ tasks , updateTask}) { }; return ( -
-

Upcoming Tasks

+
+

Upcoming Tasks

{tasks?.length ? (
{tasks.map((task) => { + const remainingTime = new Date(task.dueDate) - now; + const isOverdue = remainingTime <= 0; - const remainingTime = new Date(task.dueDate) - now; - const isOverdue = remainingTime <= 0; - - const hours = isOverdue ? 0 : Math.floor( - remainingTime / (1000 * 60 * 60) - ); - - const minutes = isOverdue ? 0 : Math.floor( - (remainingTime % (1000 * 60 * 60)) / - (1000 * 60) - ); - - const seconds = isOverdue ? 0 : Math.floor( - (remainingTime % (1000 * 60)) / 1000 - ); + const hours = isOverdue ? 0 : Math.floor(remainingTime / (1000 * 60 * 60)); + const minutes = isOverdue ? 0 : Math.floor((remainingTime % (1000 * 60 * 60)) / (1000 * 60)); + const seconds = isOverdue ? 0 : Math.floor((remainingTime % (1000 * 60)) / 1000); return ( -
- {/* Checkbox */} - handleCheckboxChange(task)} - /> - - {/* Content */} -
-

- {task.title} -

- -
- + handleCheckboxChange(task)} + /> + +
+

- {task.priority} - - - {task.dueDate && ( - - {new Date(task.dueDate).toLocaleDateString("en-US", { - weekday: "short", - })} - - )} - - {/*Disply Remaining Time */} - {task.dueDate && ( - - {isOverdue - ? "Overdue" - : `${hours}h ${minutes}m ${seconds}s left`} + {task.title} +

+ +
+ + {task.priority} - )} + {task.dueDate && ( + + {new Date(task.dueDate).toLocaleDateString("en-US", { + weekday: "short", + })} + + )} + + {task.dueDate && ( + + {isOverdue ? "Overdue" : `${hours}h ${minutes}m ${seconds}s left`} + + )} +
-
- ) })} + ); + })}
) : ( -

+

No upcoming tasks.

)} -
+
{durationModalTask && ( -
-
-

+
+
+

Complete Task

-

+

How long did you actually take to complete " - {durationModalTask.title}"? + {durationModalTask.title}"?

setActualDuration(e.target.value)} - className="w-full p-2 border border-soft rounded-lg text-black" + className="w-full px-4 py-2.5 text-sm border border-slate-200 dark:border-slate-700 bg-transparent text-slate-900 dark:text-white rounded-xl outline-none focus:ring-2 focus:ring-[#3b8ea0] transition-all box-border" placeholder="Actual duration in minutes" /> -
+
@@ -205,4 +188,6 @@ export default function TaskPreview({ tasks , updateTask}) { )}
); -} +}; + +export default TaskPreview; \ No newline at end of file diff --git a/frontend/src/components/EmptyState.jsx b/frontend/src/components/EmptyState.jsx index 54949259..5e4fd6e3 100644 --- a/frontend/src/components/EmptyState.jsx +++ b/frontend/src/components/EmptyState.jsx @@ -2,43 +2,48 @@ import { useState } from "react"; const CONFIG = { tasks: { - icon: ( - - - - - - - - - ), - heading: "No tasks yet", - subtext: "Your to-do list is empty. Add your first task and start crushing the day.", - cta: "+ Create your first task", -}, + icon: ( + + + + + + + + + ), + heading: "No tasks yet", + subtext: "Your to-do list is empty. Add your first task and start crushing the day.", + cta: "+ Create your first task", + }, routines: { icon: ( - - - - - - - - - - + + + + + + + + + + ), heading: "No routines saved", @@ -52,30 +57,32 @@ export default function EmptyState({ type = "tasks", onAction }) { const cfg = CONFIG[type] ?? CONFIG.tasks; return ( -
-
-
+
+
+ +
{cfg.icon}
-

- {cfg.heading} -

- -

- {cfg.subtext} -

+
+

+ {cfg.heading} +

+

+ {cfg.subtext} +

+
); -} +} \ No newline at end of file diff --git a/frontend/src/components/ErrorBoundary.jsx b/frontend/src/components/ErrorBoundary.jsx index f1e09644..f80d4ec9 100644 --- a/frontend/src/components/ErrorBoundary.jsx +++ b/frontend/src/components/ErrorBoundary.jsx @@ -7,12 +7,10 @@ class ErrorBoundary extends React.Component { } static getDerivedStateFromError() { - // Update state so the next render will show the fallback UI. return { hasError: true }; } componentDidCatch(error, errorInfo) { - // Log the error to an error reporting service or console console.error("ErrorBoundary caught an error:", error, errorInfo); this.setState({ error: error, @@ -31,36 +29,45 @@ class ErrorBoundary extends React.Component { render() { if (this.state.hasError) { return ( -
-
-
-
- - +
+
+
+ +
+
+
+ +
-

Oops! Something went wrong

-

- We're sorry, but an unexpected error occurred. The application has recovered from a crash, but you might need to refresh or return to the dashboard. + +

+ Oops! Something went wrong +

+ +

+ An unexpected layout or runtime crash was caught. The system has safely isolated the incident, but you may need to refresh your view block or head back to the dashboard layout.

+ {this.state.error && ( -
-

+

+

{this.state.error.toString()}

)} +
@@ -74,4 +81,4 @@ class ErrorBoundary extends React.Component { } } -export default ErrorBoundary; +export default ErrorBoundary; \ No newline at end of file diff --git a/frontend/src/components/FeedbackForm.jsx b/frontend/src/components/FeedbackForm.jsx index 43df9d76..0fa96d2c 100644 --- a/frontend/src/components/FeedbackForm.jsx +++ b/frontend/src/components/FeedbackForm.jsx @@ -46,7 +46,6 @@ export default function FeedbackForm({ onClose }) { return; } setLoading(true); - // Simulated submission — connect to backend/email API later await new Promise((res) => setTimeout(res, 1000)); console.log("Feedback submitted:", form); setLoading(false); @@ -61,19 +60,29 @@ export default function FeedbackForm({ onClose }) { if (submitted) { return ( -
-
-
-

Thank you!

-

+

+
+
+ ✓ +
+

+ Thank you! +

+

Your feedback has been received. We'll look into it shortly.

-
- {onClose && ( - )} @@ -84,26 +93,32 @@ export default function FeedbackForm({ onClose }) { } return ( -
-
- {/* Header */} -
+
+
+
-

Feedback & Bug Report

-

Help us improve DailyForge

+

+ Feedback & Bug Report +

+

+ Help us improve DailyForge +

{onClose && ( - )}
-
- {/* Email */} -
-