Skip to content

270 main dashboard makeover#272

Merged
InfinityBowman merged 19 commits into
mainfrom
270-main-dashboard-makeover
Jan 10, 2026
Merged

270 main dashboard makeover#272
InfinityBowman merged 19 commits into
mainfrom
270-main-dashboard-makeover

Conversation

@InfinityBowman
Copy link
Copy Markdown
Owner

@InfinityBowman InfinityBowman commented Jan 10, 2026

Summary by CodeRabbit

  • New Features

    • Redesigned dashboard with improved layout, stats overview, and activity tracking
    • Added quick-action shortcuts for common workflows
    • Introduced activity feed showing recent project updates
    • Enhanced project statistics with visual progress indicators
  • Bug Fixes

    • Improved loading state handling to prevent UI flickering when cached data is available
  • Performance

    • Added project statistics caching for faster dashboard loading

✏️ Tip: You can customize this high-level summary in your review settings.

@InfinityBowman InfinityBowman linked an issue Jan 10, 2026 that may be closed by this pull request
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented Jan 10, 2026

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
corates e015681 Commit Preview URL Jan 10 2026, 07:22 PM

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 10, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

📝 Walkthrough

Walkthrough

Implements a comprehensive dashboard redesign with modular component architecture, adding 12+ new dashboard components, routing updates, project stats persistence, and three settings design mocks. Includes planning documents for the redesign and study counts architecture, alongside refactored Dashboard structure, enhanced project store with caching, and improved auth loading state.

Changes

Cohort / File(s) Summary
Planning Documents
packages/docs/plans/dashboard-redesign.md, packages/docs/plans/study-counts-architecture.md
Added two comprehensive architectural planning documents: dashboard redesign outlines phased implementation with file changes and timeline; study counts architecture details two-phase approach for resolving study count display with lazy loading and notifications.
Dashboard Component Restructure
packages/web/src/components/dashboard/index.js, packages/web/src/components/dashboard/Dashboard.jsx, packages/web/src/components/dashboard/DashboardHeader.jsx, packages/web/src/components/dashboard/DashboardSkeleton.jsx
Reorganized Dashboard from single file to modular folder structure with barrel export, main Dashboard container with data hooks and AnimationContext, DashboardHeader with user greeting and actions, and DashboardSkeleton loading UI.
Dashboard Sub-components
packages/web/src/components/dashboard/StatsRow.jsx, packages/web/src/components/dashboard/ProgressCard.jsx, packages/web/src/components/dashboard/ProjectCard.jsx, packages/web/src/components/dashboard/ProjectsSection.jsx, packages/web/src/components/dashboard/QuickActions.jsx, packages/web/src/components/dashboard/ActivityFeed.jsx, packages/web/src/components/dashboard/LocalAppraisalCard.jsx, packages/web/src/components/dashboard/LocalAppraisalsSection.jsx, packages/web/src/components/dashboard/useInitialAnimation.js
Added 9 new dashboard feature components: stats display, progress visualization, project cards and grid, quick actions, activity feed, local appraisal cards and section, plus useInitialAnimation hook for session-scoped animations.
Dashboard Mocks & Index
packages/web/src/components/mocks/DashboardMock.jsx, packages/web/src/components/mocks/MockIndex.jsx
Introduced comprehensive DashboardMock with mock data and full layout implementation; updated MockIndex to feature Dashboard mock and reorganized settings mocks into dedicated section.
Settings Mock Components
packages/web/src/components/mocks/SettingsMockBento.jsx, packages/web/src/components/mocks/SettingsMockCombined.jsx, packages/web/src/components/mocks/SettingsMockMinimal.jsx
Added three settings design variations (Bento-style grid, combined profile/settings, minimal Swiss-style) as SolidJS mock components for design exploration and reference.
Routing & App Structure
packages/web/src/Routes.jsx
Updated Dashboard import path from ./components/Dashboard.jsx to ./components/dashboard/index.js; added lazy-loaded mock routes for dashboard and settings; changed local checklist route from wildcard /checklist/\\* to /checklist.
State & Store Enhancements
packages/web/src/stores/projectStore.js
Added localStorage-based project stats persistence with projectStats field, getProjectStats() method, and auto-caching of studyCount/completedCount when project data updates.
Auth & Subscription State
packages/web/src/api/better-auth-store.js, packages/web/src/primitives/useSubscription.js
Enhanced authLoading with cache-aware logic to prevent UI flashing; added isFetching property to useSubscription and refined loading to reflect only pending state without cached data.
Checklist Components
packages/web/src/components/checklist/CreateLocalChecklist.jsx, packages/web/src/components/checklist/LocalChecklistView.jsx
Updated checklist components to seed initial type from URL search parameters; integrated useSearchParams hook to pass type prop through component hierarchy.
Styling & Utilities
packages/web/src/global.css
Added new CSS animation keyframes: fade-up (opacity + upward translation) and stat-rise (opacity + upward translation + scale).
Minor Updates
packages/web/src/components/dev/DevImportProject.jsx, packages/web/src/components/project/ProjectsPanel.jsx
Updated DevImportProject textarea Tailwind class; enhanced ProjectsPanel to access subscription quotas and pass project count to ContactPrompt for quota-aware rendering.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Dashboard as Dashboard Container
    participant useDashboardData as useDashboardData Hook
    participant Auth as Auth Store
    participant Subscription as Subscription API
    participant ProjectStore as Project Store
    participant Components as Dashboard Components

    User->>Dashboard: Load Dashboard
    Dashboard->>useDashboardData: Derive dashboard state
    useDashboardData->>Auth: Get auth status & user
    useDashboardData->>Subscription: Fetch subscription/quotas
    useDashboardData->>ProjectStore: Get projects & cached stats
    
    Subscription-->>useDashboardData: Return subscription data
    Auth-->>useDashboardData: Return auth status
    ProjectStore-->>useDashboardData: Return projects + stats
    
    useDashboardData->>useDashboardData: Compute stats (projectCount,<br/>completedStudies, etc.)
    useDashboardData-->>Dashboard: Return derived state
    
    Dashboard->>Dashboard: Setup AnimationContext
    Dashboard->>Components: Render conditional layout<br/>(header, stats, projects,<br/>activity, progress)
    
    Components->>Components: Apply animations from<br/>AnimationContext
    Components-->>User: Display dashboard UI
Loading
sequenceDiagram
    participant ProjectStore as Project Store
    participant Frontend as Frontend (setProjectData)
    participant LocalStorage as localStorage
    participant Components as Dashboard Components

    Frontend->>ProjectStore: Update project data (studies)
    ProjectStore->>ProjectStore: computeProjectStats()<br/>(studyCount,<br/>completedCount)
    ProjectStore->>ProjectStore: Update projectStats[projectId]<br/>+ lastUpdated
    ProjectStore->>LocalStorage: persistStats(projectStats)
    LocalStorage-->>ProjectStore: Persisted ✓
    
    ProjectStore-->>Frontend: Data updated
    
    Components->>ProjectStore: getProjectStats(projectId)
    ProjectStore-->>Components: Return cached stats
    Components->>Components: Render stats in<br/>ProgressCard/ProjectCard
    Components-->>User: Display updated counts
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: a comprehensive dashboard redesign implementation with new components, routing updates, store enhancements, and planning documents.
Docstring Coverage ✅ Passed Docstring coverage is 84.38% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Fix all issues with AI agents
In @packages/web/src/components/checklist/CreateLocalChecklist.jsx:
- Around line 29-30: The render-time console.log in CreateLocalChecklist.jsx
(the line logging 'typeOptions') should be removed or guarded; either delete the
console.log or wrap it in a development-only conditional (e.g., check
process.env.NODE_ENV === 'development') or move the log into a useEffect inside
the CreateLocalChecklist component so it only runs once and not on every render;
update the reference to the variable name typeOptions and ensure any remaining
debug logging uses the dev-only guard.

In @packages/web/src/components/dashboard/Dashboard.jsx:
- Around line 82-95: The activities creation in createMemo currently slices
projectList before sorting, so newer projects beyond the first five are
excluded; fix by mapping projectList to activity objects (using project.name,
project.updatedAt/createdAt) then sorting that full list by timestamp (newest
first) and finally slicing the top 5; update the createMemo block that defines
activities to perform map -> sort -> slice in that order so the activity feed
shows the most recent items.

In @packages/web/src/components/dashboard/ProjectsSection.jsx:
- Around line 116-148: The handler handleDeleteProject is using raw fetch and
manual JSON/error parsing; replace the fetch call with the shared apiFetch
helper (call apiFetch(`/api/orgs/${project.orgId}/projects/${targetProjectId}`,
{ method: 'DELETE', credentials: 'include' })) and remove the response.ok /
manual JSON parsing and throw logic, letting apiFetch surface errors/toasts;
keep the surrounding try/catch, retain queryClient.invalidateQueries({ queryKey:
queryKeys.projects.all }) and the success toast (or remove it if apiFetch
already shows success per project conventions), and still import/await
handleError in the catch block as before.

In @packages/web/src/components/mocks/DashboardMock.jsx:
- Around line 142-146: ProgressArc computes percentage and offset using
props.total which can be 0 causing NaN/Infinity; update the percentage
calculation in ProgressArc (the percentage() helper) to guard against zero or
falsy totals (e.g., return 0 when props.total is 0 or not a finite number) and
ensure offset() uses that safe percentage value (or falls back to 100/0
appropriately); keep the fix localized to the percentage() and offset() helpers
so other behavior remains unchanged.

In @packages/web/src/Routes.jsx:
- Line 44: Remove the dead standalone string literal "('');" found in Routes.jsx
(it's a no-op leftover) — locate the Routes component or top-level JSX file
where the expression appears and delete that stray string expression so no
extraneous statements remain in the module.
🧹 Nitpick comments (19)
packages/web/src/components/checklist/CreateLocalChecklist.jsx (1)

20-23: Validate searchParams.type against getChecklistTypeOptions() before seeding state.

Right now any arbitrary ?type= value becomes the selected value even if it’s not registered (select can end up with no matching option). Consider whitelisting against typeOptions.

Proposed fix
   const { createChecklist, savePdf } = localChecklistsStore;

   const [name, setName] = createSignal('');
-  const [checklistType, setChecklistType] = createSignal(
-    searchParams.type || DEFAULT_CHECKLIST_TYPE,
-  );
+  const typeOptions = getChecklistTypeOptions();
+  const urlType = searchParams.type;
+  const initialType = typeOptions.some(o => o.value === urlType)
+    ? urlType
+    : DEFAULT_CHECKLIST_TYPE;
+  const [checklistType, setChecklistType] = createSignal(initialType);
   const [pdfFile, setPdfFile] = createSignal(null);
   const [creating, setCreating] = createSignal(false);
   const [error, setError] = createSignal(null);

-  const typeOptions = getChecklistTypeOptions();
+  // typeOptions now declared above (used for initialType validation)

Also applies to: 28-28

packages/web/src/components/checklist/LocalChecklistView.jsx (1)

11-12: type={searchParams.type} is currently redundant (CreateLocalChecklist ignores it).

Either:

  1. Remove useSearchParams + the type prop here, since CreateLocalChecklist already reads searchParams.type internally; or
  2. Update CreateLocalChecklist to accept a type prop and prefer it over URL state (keeping Solid’s “no props destructuring” rule). As per coding guidelines, avoid prop-drilling shared state unless it’s truly local config.

Also applies to: 22-23, 178-178

packages/web/src/api/better-auth-store.js (1)

871-871: Consider documenting the dual loading state pattern.

The export now uses isAuthLoading (cache-aware) while internal code at lines 169, 199, 225, and 269 continues to use the raw authLoading() from the session. This dual pattern is intentional and correct:

  • Internal usage (raw authLoading()): Needed to detect actual session state for deciding when to use cached data, prevent concurrent refreshes, and avoid premature cache clearing.
  • External usage (isAuthLoading): Provides cache-aware loading state to UI components, preventing loading flashes when cached data exists.

However, this subtle distinction could benefit from a comment near the isAuthLoading definition (around line 178) explaining why the internal implementation still references the raw authLoading() signal for state management decisions.

📝 Suggested documentation addition
  // Enhanced authLoading that accounts for cached data
  // If we have cached user data, we're not "loading" from UI perspective
+ // NOTE: Internal code still uses raw authLoading() for state decisions
+ // (e.g., when to use cached data, prevent concurrent refreshes).
+ // This function is exported for UI components to avoid loading flashes.
  const isAuthLoading = () => {
packages/web/src/components/mocks/SettingsMockMinimal.jsx (3)

93-98: Redundant transform styling in toggle

The toggle has both a Tailwind class (translate-x-5.5) and an inline style with translateX. The inline style overrides the class, making the Tailwind class ineffective. Consider using only one approach.

🔧 Suggested fix - use only inline style (simpler for dynamic values)
       <div
-        class={`absolute top-0.5 h-5 w-5 rounded-full bg-white shadow-sm transition-transform duration-200 ${
-          props.checked ? 'left-0.5 translate-x-5.5' : 'left-0.5'
-        }`}
+        class='absolute left-0.5 top-0.5 h-5 w-5 rounded-full bg-white shadow-sm transition-transform duration-200'
         style={{ transform: props.checked ? 'translateX(22px)' : 'translateX(0)' }}
       />

343-367: Consider using For component instead of Array.map()

Per coding guidelines, prefer Solid's For component for rendering lists. This is a mock component so it's low priority, but worth noting for consistency.

🔧 Suggested refactor
-              {[
-                  { label: 'Projects', used: 8, limit: 15 },
-                  { label: 'Collaborators', used: 12, limit: 25 },
-                  { label: 'Storage', used: 2.4, limit: 10, suffix: 'GB' },
-                ].map(stat => (
+              <For each={[
+                  { label: 'Projects', used: 8, limit: 15 },
+                  { label: 'Collaborators', used: 12, limit: 25 },
+                  { label: 'Storage', used: 2.4, limit: 10, suffix: 'GB' },
+                ]}>
+                {stat => (
                   <div class='rounded-xl border border-neutral-100 bg-neutral-50/50 p-6'>
                     ...
                   </div>
-                ))}
+                )}
+              </For>

467-521: Same Array.map() pattern - consider For component

Same suggestion applies here for the integrations list. As per coding guidelines, use Solid's For component for rendering lists instead of Array.map().

packages/docs/plans/dashboard-redesign.md (1)

1-528: Well-structured dashboard redesign plan

This is a comprehensive planning document that covers:

  • Clear user state matrix handling all authentication/subscription scenarios
  • Phased implementation approach with dependencies
  • Concrete file changes aligned with actual PR implementation
  • Thoughtful migration strategy using feature flags
  • Success metrics and testing requirements

The plan aligns well with the dashboard components introduced in this PR.

Minor markdown improvements (optional):

  • Add language specifiers to code blocks at lines 72, 187, 371, 392, 400 (e.g., ```plaintext or ```bash)
  • Line 452: "Logged out user" → "Logged-out user" (hyphenated compound adjective)
packages/web/src/components/mocks/SettingsMockBento.jsx (2)

10-31: Several unused icon imports.

Icons FiUser, FiMail, FiUsers, FiChevronRight are imported but not used in the component. This is minor for a mock file but worth cleaning up.

🧹 Remove unused imports
 import {
-  FiUser,
   FiCreditCard,
   FiShield,
   FiBell,
   FiLink,
   FiMonitor,
   FiMoon,
   FiSun,
   FiCheck,
-  FiChevronRight,
   FiArrowLeft,
-  FiMail,
   FiCloud,
   FiHardDrive,
   FiZap,
-  FiUsers,
   FiFolder,
   FiLock,
   FiSmartphone,
   FiGlobe,
 } from 'solid-icons/fi';

480-486: Consider preloading the external font to avoid FOUC.

Loading fonts via @import inside a component's <style> tag can cause a flash of unstyled content (FOUC). For mock pages this is acceptable, but if this pattern is used in production, consider adding a <link rel="preload"> in the document head or using a font-loading strategy.

packages/web/src/components/dashboard/LocalAppraisalCard.jsx (1)

22-37: Extract formatRelativeTime to a shared utility.

This function is duplicated in ActivityFeed.jsx, ProjectCard.jsx, and now LocalAppraisalCard.jsx. Consider extracting it to a shared utility (e.g., @/lib/dateUtils.js) to maintain consistency and reduce duplication.

📦 Suggested shared utility

Create packages/web/src/lib/dateUtils.js:

/**
 * Format a relative time string
 * @param {Date|string|number} date
 * @returns {string}
 */
export function formatRelativeTime(date) {
  if (!date) return '';
  const now = new Date();
  const then = new Date(date);
  const diffMs = now - then;
  const diffMins = Math.floor(diffMs / 60000);
  const diffHours = Math.floor(diffMins / 60);
  const diffDays = Math.floor(diffHours / 24);

  if (diffMins < 1) return 'Just now';
  if (diffMins < 60) return `${diffMins}m ago`;
  if (diffHours < 24) return `${diffHours}h ago`;
  if (diffDays < 7) return `${diffDays}d ago`;
  if (diffDays < 30) return `${Math.floor(diffDays / 7)}w ago`;
  return then.toLocaleDateString();
}

Then import in each component:

-function formatRelativeTime(date) { ... }
+import { formatRelativeTime } from '@/lib/dateUtils.js';
packages/web/src/components/dashboard/ActivityFeed.jsx (2)

15-28: Same formatRelativeTime duplication.

As noted in LocalAppraisalCard.jsx, this function should be extracted to a shared utility.


33-57: Consider memoizing icon maps or moving outside component.

The iconMap and bgMap objects are recreated on every render of ActivityIcon. Since these are static, they could be defined at module scope for a minor performance improvement.

♻️ Move static maps to module scope
+const ACTIVITY_ICONS = {
+  study: <FiFileText class='h-4 w-4' />,
+  project: <FiFolder class='h-4 w-4' />,
+  complete: <FiCheck class='h-4 w-4' />,
+  user: <FiUser class='h-4 w-4' />,
+  default: <FiClock class='h-4 w-4' />,
+};
+
+const ACTIVITY_BG = {
+  study: 'bg-blue-100 text-blue-600',
+  project: 'bg-amber-100 text-amber-600',
+  complete: 'bg-emerald-100 text-emerald-600',
+  user: 'bg-violet-100 text-violet-600',
+  default: 'bg-stone-100 text-stone-600',
+};
+
 function ActivityIcon(props) {
-  const iconMap = { ... };
-  const bgMap = { ... };
-
   return (
     <div
-      class={`flex h-8 w-8 shrink-0 items-center justify-center rounded-full ${bgMap[props.type] || bgMap.default}`}
+      class={`flex h-8 w-8 shrink-0 items-center justify-center rounded-full ${ACTIVITY_BG[props.type] || ACTIVITY_BG.default}`}
     >
-      {iconMap[props.type] || iconMap.default}
+      {ACTIVITY_ICONS[props.type] || ACTIVITY_ICONS.default}
     </div>
   );
 }
packages/web/src/components/dashboard/QuickActions.jsx (1)

53-81: Consider memoizing the actions array.

The actions() function recreates the array and JSX icons on every access. For a small static list this is acceptable, but you could use createMemo or move the config to module scope for consistency with the pattern used elsewhere.

♻️ Optional: memoize with createMemo
+import { For, useContext, createMemo } from 'solid-js';
 
 export function QuickActions(props) {
   const animation = useContext(AnimationContext);
 
-  const actions = () => [
+  const actions = createMemo(() => [
     {
       id: 'robins-i',
       // ... rest of array
     },
-  ];
+  ]);
packages/web/src/components/dashboard/DashboardHeader.jsx (1)

20-23: Consider using createMemo for derived firstName.

For consistency with other dashboard components that use createMemo for derived values, firstName could be wrapped in createMemo. This is a minor optimization since it's a simple string operation.

♻️ Optional: use createMemo
+import { Show, useContext, createMemo } from 'solid-js';
 
 export function DashboardHeader(props) {
   const animation = useContext(AnimationContext);
-  const firstName = () => {
+  const firstName = createMemo(() => {
     const name = props.user?.name || '';
     return name.split(' ')[0] || '';
-  };
+  });
packages/docs/plans/study-counts-architecture.md (1)

197-214: Add language specifier to the fenced code block.

The data flow diagram code block at line 197 is missing a language specifier, which triggers a markdownlint warning (MD040). Consider adding text or plaintext as the language.

Proposed fix
-```
+```text
 Dashboard Load:
   API returns projects (no stats or stale stats from SQL cache)
packages/web/src/components/mocks/SettingsMockCombined.jsx (1)

761-784: Use For component instead of .map() for rendering lists.

Per coding guidelines, SolidJS components should use For for rendering lists instead of Array.map().

Proposed fix
               {/* Other integrations */}
               <div class='mt-4 space-y-3'>
-                {[
-                  { name: 'Zotero', description: 'Sync your reference library', available: false },
-                  {
-                    name: 'Mendeley',
-                    description: 'Import references and annotations',
-                    available: false,
-                  },
-                ].map(integration => (
+                <For each={[
+                  { name: 'Zotero', description: 'Sync your reference library', available: false },
+                  { name: 'Mendeley', description: 'Import references and annotations', available: false },
+                ]}>
+                  {integration => (
                   <div class='flex items-center justify-between rounded-xl border border-gray-200 p-5 opacity-60'>
                     ...
                   </div>
-                ))}
+                  )}
+                </For>
               </div>
packages/web/src/components/dashboard/ProjectCard.jsx (1)

79-94: Duplicate formatRelativeTime utility across multiple files.

This function is duplicated in ActivityFeed.jsx (lines 15-28) and LocalAppraisalCard.jsx (lines 23-28). Consider extracting to a shared utility in @/lib/ or @primitives/.

packages/web/src/stores/projectStore.js (1)

91-101: Side effect (localStorage write) inside produce() callback.

Calling persistStats() inside the produce() callback works but mixes a side effect with state mutation. Consider moving the persistence call after setStore completes for clearer separation.

Alternative pattern
   function setProjectData(projectId, data) {
+    let shouldPersist = false;
     setStore(
       produce(s => {
         // ... existing code ...
         if (data.studies !== undefined) {
           s.projects[projectId].studies = data.studies;
           const stats = computeProjectStats(data.studies);
           s.projectStats[projectId] = {
             ...stats,
             lastUpdated: Date.now(),
           };
-          persistStats(s.projectStats);
+          shouldPersist = true;
         }
       }),
     );
+    if (shouldPersist) {
+      persistStats(store.projectStats);
+    }
   }
packages/web/src/components/mocks/DashboardMock.jsx (1)

15-15: Unused import: createEffect.

createEffect is imported but not used in this file.

Proposed fix
-import { For, Show, createSignal, createEffect, onMount } from 'solid-js';
+import { For, Show, createSignal, onMount } from 'solid-js';

Comment on lines 29 to 30
console.log('typeOptions', typeOptions);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove (or DEV-guard) render-time console.log.

Proposed fix
-  console.log('typeOptions', typeOptions);
+  if (import.meta.env.DEV) console.log('typeOptions', typeOptions);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.log('typeOptions', typeOptions);
if (import.meta.env.DEV) console.log('typeOptions', typeOptions);
🤖 Prompt for AI Agents
In @packages/web/src/components/checklist/CreateLocalChecklist.jsx around lines
29 - 30, The render-time console.log in CreateLocalChecklist.jsx (the line
logging 'typeOptions') should be removed or guarded; either delete the
console.log or wrap it in a development-only conditional (e.g., check
process.env.NODE_ENV === 'development') or move the log into a useEffect inside
the CreateLocalChecklist component so it only runs once and not on every render;
update the reference to the variable name typeOptions and ensure any remaining
debug logging uses the dev-only guard.

Comment on lines +82 to +95
// Generate recent activity from projects
const activities = createMemo(() => {
const projectList = projects() || [];
return projectList
.slice(0, 5)
.map(project => ({
type: 'project',
title: project.name,
subtitle: 'was updated',
timestamp: project.updatedAt || project.createdAt,
}))
.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
.slice(0, 5);
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Activities slice occurs before sort, potentially missing recent items.

The current logic slices the first 5 projects, maps them to activities, then sorts by timestamp. If the most recently updated projects are beyond index 4, they won't appear in the activity feed.

Proposed fix
   const activities = createMemo(() => {
     const projectList = projects() || [];
     return projectList
-      .slice(0, 5)
       .map(project => ({
         type: 'project',
         title: project.name,
         subtitle: 'was updated',
         timestamp: project.updatedAt || project.createdAt,
       }))
       .sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
       .slice(0, 5);
   });
🤖 Prompt for AI Agents
In @packages/web/src/components/dashboard/Dashboard.jsx around lines 82 - 95,
The activities creation in createMemo currently slices projectList before
sorting, so newer projects beyond the first five are excluded; fix by mapping
projectList to activity objects (using project.name,
project.updatedAt/createdAt) then sorting that full list by timestamp (newest
first) and finally slicing the top 5; update the createMemo block that defines
activities to perform map -> sort -> slice in that order so the activity feed
shows the most recent items.

Comment on lines +116 to +148
const handleDeleteProject = async targetProjectId => {
const confirmed = await confirmDialog.open({
title: 'Delete Project',
description:
'Are you sure you want to delete this entire project? This action cannot be undone.',
confirmText: 'Delete Project',
variant: 'danger',
});
if (!confirmed) return;

const project = projects()?.find(p => p.id === targetProjectId);
if (!project?.orgId) {
showToast.error('Error', 'Unable to find project organization');
return;
}

try {
const response = await fetch(
`${API_BASE}/api/orgs/${project.orgId}/projects/${targetProjectId}`,
{ method: 'DELETE', credentials: 'include' },
);
if (!response.ok) {
const data = await response.json().catch(() => ({}));
throw new Error(data.error || 'Failed to delete project');
}

queryClient.invalidateQueries({ queryKey: queryKeys.projects.all });
showToast.success('Project Deleted', 'The project has been deleted successfully');
} catch (err) {
const { handleError } = await import('@/lib/error-utils.js');
await handleError(err, { toastTitle: 'Delete Failed' });
}
};
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Refactor to use apiFetch instead of raw fetch.

The deletion handler uses raw fetch and manually handles JSON parsing and error responses. As per coding guidelines, all frontend API calls should use apiFetch to automatically handle JSON parsing, errors, and toast notifications.

♻️ Refactor to use apiFetch
  const handleDeleteProject = async targetProjectId => {
    const confirmed = await confirmDialog.open({
      title: 'Delete Project',
      description:
        'Are you sure you want to delete this entire project? This action cannot be undone.',
      confirmText: 'Delete Project',
      variant: 'danger',
    });
    if (!confirmed) return;

    const project = projects()?.find(p => p.id === targetProjectId);
    if (!project?.orgId) {
      showToast.error('Error', 'Unable to find project organization');
      return;
    }

    try {
-     const response = await fetch(
-       `${API_BASE}/api/orgs/${project.orgId}/projects/${targetProjectId}`,
-       { method: 'DELETE', credentials: 'include' },
-     );
-     if (!response.ok) {
-       const data = await response.json().catch(() => ({}));
-       throw new Error(data.error || 'Failed to delete project');
-     }
+     const { apiFetch } = await import('@/lib/api-utils.js');
+     await apiFetch(`${API_BASE}/api/orgs/${project.orgId}/projects/${targetProjectId}`, {
+       method: 'DELETE',
+     });

      queryClient.invalidateQueries({ queryKey: queryKeys.projects.all });
      showToast.success('Project Deleted', 'The project has been deleted successfully');
    } catch (err) {
      const { handleError } = await import('@/lib/error-utils.js');
      await handleError(err, { toastTitle: 'Delete Failed' });
    }
  };

As per coding guidelines.

🤖 Prompt for AI Agents
In @packages/web/src/components/dashboard/ProjectsSection.jsx around lines 116 -
148, The handler handleDeleteProject is using raw fetch and manual JSON/error
parsing; replace the fetch call with the shared apiFetch helper (call
apiFetch(`/api/orgs/${project.orgId}/projects/${targetProjectId}`, { method:
'DELETE', credentials: 'include' })) and remove the response.ok / manual JSON
parsing and throw logic, letting apiFetch surface errors/toasts; keep the
surrounding try/catch, retain queryClient.invalidateQueries({ queryKey:
queryKeys.projects.all }) and the success toast (or remove it if apiFetch
already shows success per project conventions), and still import/await
handleError in the catch block as before.

Comment on lines +142 to +146
function ProgressArc(props) {
const percentage = () => Math.round((props.completed / props.total) * 100);
const circumference = 2 * Math.PI * 36;
const offset = () => circumference - (percentage() / 100) * circumference;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Potential division by zero in ProgressArc.

If props.total is 0, the percentage calculation will result in NaN or Infinity.

Proposed fix
 function ProgressArc(props) {
-  const percentage = () => Math.round((props.completed / props.total) * 100);
+  const percentage = () => {
+    if (!props.total || props.total === 0) return 0;
+    return Math.round((props.completed / props.total) * 100);
+  };
   const circumference = 2 * Math.PI * 36;
🤖 Prompt for AI Agents
In @packages/web/src/components/mocks/DashboardMock.jsx around lines 142 - 146,
ProgressArc computes percentage and offset using props.total which can be 0
causing NaN/Infinity; update the percentage calculation in ProgressArc (the
percentage() helper) to guard against zero or falsy totals (e.g., return 0 when
props.total is 0 or not a finite number) and ensure offset() uses that safe
percentage value (or falls back to 100/0 appropriately); keep the fix localized
to the percentage() and offset() helpers so other behavior remains unchanged.

Comment thread packages/web/src/Routes.jsx Outdated
const SettingsMockMinimal = lazy(() => import('@/components/mocks/SettingsMockMinimal.jsx'));
const SettingsMockCombined = lazy(() => import('@/components/mocks/SettingsMockCombined.jsx'));
const DashboardMock = lazy(() => import('@/components/mocks/DashboardMock.jsx'));
('');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove dead code: standalone string literal.

Line 44 contains a standalone string expression (''); which does nothing and appears to be a typo or leftover from editing.

🧹 Remove dead code
 const DashboardMock = lazy(() => import('@/components/mocks/DashboardMock.jsx'));
-('');
 // Code-split admin routes - loaded only when navigating to /admin/*
🤖 Prompt for AI Agents
In @packages/web/src/Routes.jsx at line 44, Remove the dead standalone string
literal "('');" found in Routes.jsx (it's a no-op leftover) — locate the Routes
component or top-level JSX file where the expression appears and delete that
stray string expression so no extraneous statements remain in the module.

@InfinityBowman InfinityBowman merged commit 364ab83 into main Jan 10, 2026
3 checks passed
@InfinityBowman InfinityBowman deleted the 270-main-dashboard-makeover branch January 11, 2026 08:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Main dashboard makeover

2 participants