Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: progressive loading of Analytics page #1447

Merged
merged 5 commits into from
Jan 28, 2025

Conversation

ab-smith
Copy link
Contributor

@ab-smith ab-smith commented Jan 28, 2025

Note to self: could interfere with the tests

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a loading spinner to improve user experience during data retrieval.
    • Enhanced analytics page with dynamic data loading and error handling.
  • Improvements

    • Implemented more robust data fetching for metrics and counters.
    • Added conditional rendering to handle loading states.
    • Simplified data structures for the analytics page.
  • Performance

    • Improved error handling for API data retrieval.
    • Optimized data streaming for analytics components.

Copy link

coderabbitai bot commented Jan 28, 2025

Walkthrough

This pull request introduces significant changes to the analytics page in the frontend, focusing on improving data fetching and loading state management. The modifications include restructuring the server-side data retrieval logic, adding new asynchronous functions for fetching metrics and counters, and implementing a loading spinner component. The changes aim to enhance error handling and provide a more robust user experience by adding loading states and improving data management.

Changes

File Change Summary
frontend/src/routes/(app)/(internal)/analytics/+page.server.ts - Removed constants and interfaces
- Added getCounters() and getMetrics() async functions
- Modified load function to use new stream-based data retrieval
frontend/src/routes/(app)/(internal)/analytics/+page.svelte - Removed Counters interface
- Imported LoadingSpinner component
- Replaced static data with async fetching and added loading states
frontend/src/lib/components/utils/LoadingSpinner.svelte - Created new loading spinner component
- Implemented animated spinner with Flexbox centering

Possibly related PRs

Suggested labels

perf

Poem

🐰 Metrics dance, counters spin round,
Loading spinner twirls without a sound.
Code refactored, errors kept at bay,
Streamlined analytics find their way.
Rabbit's code hops with grace today! 🌟


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@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: 1

🧹 Nitpick comments (3)
frontend/src/routes/(app)/(internal)/analytics/+page.server.ts (1)

108-110: Consider adding TypeScript types for the metrics data.

The metrics data structure is used extensively in the UI. Adding type definitions would improve maintainability and catch potential type-related bugs early.

interface Metrics {
  controls: {
    total: number;
    active: number;
    deprecated: number;
    to_do: number;
    in_progress: number;
    on_hold: number;
    p1: number;
    eta_missed: number;
  };
  compliance: {
    used_frameworks: number;
    audits: number;
    active_audits: number;
    progress_avg: number;
    non_compliant_items: number;
    evidences: number;
  };
  risk: {
    assessments: number;
    scenarios: number;
    threats: number;
    acceptances: number;
  };
  csf_functions: Array<{ name: string; value: number }>;
  audits_stats: {
    names: string[];
    data: number[][];
    uuids: string[];
  };
}

// Then use it in the return type
stream: {
  metrics: Promise<Metrics>
}
frontend/src/routes/(app)/(internal)/analytics/+page.svelte (2)

20-20: Add TypeScript type for the metrics variable.

The metrics variable and its reactive subscription look good, but adding type information would improve maintainability.

+import type { Metrics } from '$lib/types/analytics';
 import LoadingSpinner from './LoadingSpinner.svelte';

-let metrics;
+let metrics: Metrics | undefined;

Also applies to: 32-41


150-334: Improve href values and loading state presentation.

The conditional rendering and loading state implementation look good, but there are a few improvements that could enhance the user experience:

  1. Center the loading spinner and add a container:
-    <div class="col-span-3 lg:col-span-1">
-      <div>Refreshing data ..</div>
-      <LoadingSpinner />
-    </div>
+    <div class="flex flex-col items-center justify-center p-8 space-y-4">
+      <div class="text-lg font-semibold">Refreshing analytics data...</div>
+      <LoadingSpinner />
+    </div>
  1. Fix the href for non-compliant items (currently "#"):
-    href="#"
+    href="/compliance-assessments/?status=non_compliant"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4a867cf and f9e225d.

📒 Files selected for processing (3)
  • frontend/src/routes/(app)/(internal)/analytics/+page.server.ts (2 hunks)
  • frontend/src/routes/(app)/(internal)/analytics/+page.svelte (3 hunks)
  • frontend/src/routes/(app)/(internal)/analytics/LoadingSpinner.svelte (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • frontend/src/routes/(app)/(internal)/analytics/LoadingSpinner.svelte
⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: startup-functional-test (3.12)
  • GitHub Check: startup-docker-compose-test
  • GitHub Check: enterprise-startup-functional-test (3.12)
  • GitHub Check: migrations-check (3.12)
  • GitHub Check: functional-tests (3.12, chromium)
  • GitHub Check: enterprise-startup-docker-compose-test
  • GitHub Check: enterprise-functional-tests (3.12, chromium)
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (javascript-typescript)

Copy link

@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: 0

🧹 Nitpick comments (2)
frontend/src/routes/(app)/(internal)/analytics/+page.server.ts (2)

46-58: Enhance error handling and add type safety.

While the basic error handling is good, consider these improvements:

  1. Add TypeScript types for better type safety
  2. Add HTTP status code checking
  3. Include more context in error messages
-const getMetrics = () => {
+type MetricsResponse = {
+  results: unknown;
+};
+
+const getMetrics = async (): Promise<unknown> => {
   return fetch(`${BASE_API_URL}/get_metrics/`)
     .catch((error) => {
-      console.error('Failed to fetch metrics:', error);
+      console.error(`Failed to fetch metrics from ${BASE_API_URL}/get_metrics/:`, error);
       return { json: () => ({ results: null }) };
     })
-    .then((response) => response.json())
+    .then((response) => {
+      if (!response.ok) {
+        throw new Error(`HTTP error! status: ${response.status}`);
+      }
+      return response.json() as Promise<MetricsResponse>;
+    })
     .catch((error) => {
       console.error('Failed to parse metrics response:', error);
       return { results: null };
     })
     .then((data) => data.results);
 };

Line range hint 1-120: Consider architectural improvements for consistency and maintainability.

The file would benefit from several architectural improvements:

  1. Apply consistent error handling across all fetch calls, similar to getMetrics
  2. Standardize on either .then() chains or async/await syntax
  3. Consider extracting repeated data transformations into utility functions

Example refactor for consistency:

// Create a typed fetch utility
type ApiResponse<T> = {
  results: T;
};

async function apiFetch<T>(endpoint: string): Promise<T | null> {
  return fetch(`${BASE_API_URL}${endpoint}`)
    .catch((error) => {
      console.error(`Failed to fetch ${endpoint}:`, error);
      return { json: () => ({ results: null }) };
    })
    .then((response) => {
      if (!response.ok) {
        throw new Error(`HTTP error! status: ${response.status}`);
      }
      return response.json() as Promise<ApiResponse<T>>;
    })
    .catch((error) => {
      console.error(`Failed to parse ${endpoint} response:`, error);
      return { results: null };
    })
    .then((data) => data.results);
}

// Extract time state logic
const TimeState = {
  INCOMING: { name: 'incoming', hexcolor: '#93c5fd' },
  OUTDATED: { name: 'outdated', hexcolor: '#f87171' },
  TODAY: { name: 'today', hexcolor: '#fbbf24' }
} as const;

function getTimeState(date: string) {
  const eta = new Date(date);
  if (eta.getTime() > TODAY.getTime()) {
    return TimeState.INCOMING;
  } else if (eta.getTime() < TODAY.getTime()) {
    return TimeState.OUTDATED;
  }
  return TimeState.TODAY;
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9e225d and d61427b.

📒 Files selected for processing (1)
  • frontend/src/routes/(app)/(internal)/analytics/+page.server.ts (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: startup-functional-test (3.12)
  • GitHub Check: startup-docker-compose-test
  • GitHub Check: enterprise-startup-functional-test (3.12)
  • GitHub Check: functional-tests (3.12, chromium)
  • GitHub Check: enterprise-startup-docker-compose-test
  • GitHub Check: enterprise-functional-tests (3.12, chromium)
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
frontend/src/routes/(app)/(internal)/analytics/+page.server.ts (1)

115-118: Verify streaming implementation and type safety.

The streaming implementation might need additional safeguards:

  1. Consider adding error boundaries in the consuming component to handle streaming failures gracefully
  2. Add type definitions for the streamed metrics data to replace the removed interfaces
  3. Add tests to verify the streaming behavior, especially given the PR note about test interference

Let's check for existing error boundaries and types:

✅ Verification successful

Implementation verified - error handling and type safety are properly implemented

The streaming implementation includes:

  • Server-side error handling for fetch failures
  • Client-side promise handling with loading states
  • Type safety through TypeScript interfaces
  • Graceful degradation with loading spinner
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for error boundary components and type definitions
echo "Searching for error boundary components..."
rg -l "error.*boundary" frontend/src/

echo "Searching for metrics-related type definitions..."
rg -l "type.*Metrics" frontend/src/

Length of output: 281


Script:

#!/bin/bash
# Broader search for error handling, types, and streaming implementation
echo "Checking for error handling patterns..."
rg "try.*catch|throw|Error" frontend/src/routes/\(app\)/\(internal\)/analytics/ -A 2

echo "Looking for type definitions..."
rg "interface|type.*=|export type" frontend/src/routes/\(app\)/\(internal\)/analytics/ -A 2

echo "Examining the page component..."
cat frontend/src/routes/\(app\)/\(internal\)/analytics/+page.svelte

echo "Checking streaming implementation..."
rg "getMetrics" frontend/src/routes/\(app\)/\(internal\)/analytics/ -A 5

Length of output: 19040

Copy link

@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: 1

🧹 Nitpick comments (4)
frontend/src/routes/(app)/(internal)/analytics/+page.server.ts (2)

43-52: Enhance error handling in getCounters.

While the basic error handling is good, consider these improvements:

  1. Check HTTP status codes
  2. Add type checking for the response
  3. Handle specific error types
 const getCounters = async () => {
   try {
     const response = await fetch(`${BASE_API_URL}/get_counters/`);
+    if (!response.ok) {
+      throw new Error(`HTTP error! status: ${response.status}`);
+    }
     const data = await response.json();
+    if (!data.results || typeof data.results !== 'object') {
+      throw new Error('Invalid response format');
+    }
     return data.results;
   } catch (error) {
+    if (error instanceof TypeError) {
+      console.error('Network error:', error);
+    } else {
       console.error('Failed to fetch or parse counters:', error);
+    }
     return null;
   }
 };

119-122: Add type safety and error boundaries for streams.

Consider these improvements:

  1. Define an interface for the stream object
  2. Add error boundaries in the UI component to handle failed streams gracefully
+interface DataStream {
+  metrics: Promise<MetricsData | null>;
+  counters: Promise<CountersData | null>;
+}

 return {
   // ... other properties
   stream: {
     metrics: getMetrics(),
     counters: getCounters()
-  }
+  } as DataStream
 };
frontend/src/routes/(app)/(internal)/analytics/+page.svelte (2)

32-33: Add type information for metrics variable.

While counters has proper typing, metrics lacks type information. Consider adding an interface for the metrics data structure.

+interface Metrics {
+  controls: {
+    total: number;
+    active: number;
+    deprecated: number;
+    to_do: number;
+    in_progress: number;
+    on_hold: number;
+    p1: number;
+    eta_missed: number;
+  };
+  compliance: {
+    used_frameworks: number;
+    active_audits: number;
+    audits: number;
+    progress_avg: number;
+    non_compliant_items: number;
+    evidences: number;
+  };
+  risk: {
+    assessments: number;
+    scenarios: number;
+    threats: number;
+    acceptances: number;
+  };
+  csf_functions: any[]; // Replace 'any' with proper type
+  audits_stats: {
+    names: string[];
+    data: number[][];
+    uuids: string[];
+  };
+}

-let metrics;
+let metrics: Metrics;

336-338: Improve loading and error states UI.

The current implementation could be enhanced with better loading indicators and error states.

-<div class="col-span-3 lg:col-span-1">
-  <div>Refreshing data ..</div>
-  <LoadingSpinner />
-</div>
+<div class="col-span-3 lg:col-span-1">
+  {#if metricsError}
+    <div class="text-red-500">
+      <i class="fas fa-exclamation-circle"></i>
+      Failed to load metrics: {metricsError.message}
+    </div>
+  {:else}
+    <div class="text-gray-600">
+      <LoadingSpinner />
+      <span class="ml-2">Loading metrics data...</span>
+    </div>
+  {/if}
+</div>

-<LoadingSpinner />
+{#if countersError}
+  <div class="text-red-500">
+    <i class="fas fa-exclamation-circle"></i>
+    Failed to load counters: {countersError.message}
+  </div>
+{:else}
+  <div class="text-gray-600">
+    <LoadingSpinner />
+    <span class="ml-2">Loading statistics...</span>
+  </div>
+{/if}

Also applies to: 387-387

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d61427b and d58af59.

📒 Files selected for processing (3)
  • frontend/src/lib/components/utils/LoadingSpinner.svelte (1 hunks)
  • frontend/src/routes/(app)/(internal)/analytics/+page.server.ts (2 hunks)
  • frontend/src/routes/(app)/(internal)/analytics/+page.svelte (3 hunks)
✅ Files skipped from review due to trivial changes (1)
  • frontend/src/lib/components/utils/LoadingSpinner.svelte
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: enterprise-startup-docker-compose-test
  • GitHub Check: startup-functional-test (3.12)
  • GitHub Check: startup-docker-compose-test
  • GitHub Check: enterprise-functional-tests (3.12, chromium)
  • GitHub Check: enterprise-startup-functional-test (3.12)
  • GitHub Check: functional-tests (3.12, chromium)
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
frontend/src/routes/(app)/(internal)/analytics/+page.server.ts (1)

53-62: Apply the same error handling improvements to getMetrics.

The same error handling improvements suggested for getCounters should be applied here for consistency.

Comment on lines 35 to 48
// Subscribe to the streamed metrics
$: {
if (data.stream?.metrics) {
data.stream.metrics.then((metricsData) => {
metrics = metricsData;
});
}

if (data.stream?.counters) {
data.stream.counters.then((countersData) => {
counters = countersData;
});
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling and loading state management to stream subscriptions.

The current implementation doesn't handle promise rejections or manage loading states explicitly.

+let metricsError: Error | null = null;
+let countersError: Error | null = null;
+let isLoading = true;

 $: {
   if (data.stream?.metrics) {
     data.stream.metrics.then((metricsData) => {
       metrics = metricsData;
+    }).catch((error) => {
+      console.error('Failed to load metrics:', error);
+      metricsError = error;
+    }).finally(() => {
+      isLoading = false;
     });
   }

   if (data.stream?.counters) {
     data.stream.counters.then((countersData) => {
       counters = countersData;
+    }).catch((error) => {
+      console.error('Failed to load counters:', error);
+      countersError = error;
+    }).finally(() => {
+      isLoading = false;
     });
   }
 }

Committable suggestion skipped: line range outside the PR's diff.

Copy link

@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: 2

🧹 Nitpick comments (3)
frontend/src/routes/(app)/(internal)/analytics/+page.svelte (3)

122-124: Improve loading state message and styling.

The loading state could be enhanced for better user experience:

  1. Use "Loading..." instead of "Refreshing data .." for consistency with common UI patterns
  2. Apply the same grid styling as the loaded state to prevent layout shifts
-    <div class="col-span-3 lg:col-span-1">
-        <div>Refreshing data ..</div>
+    <section id="summary" class="grid grid-cols-6 gap-2">
+        <div class="col-span-6 flex items-center justify-center">
+            <div>Loading...</div>

312-314: Apply consistent loading state pattern.

Use the same enhanced loading state pattern as suggested for metrics:

-    <div class="col-span-3 lg:col-span-1">
-        <div>Refreshing data ..</div>
-        <LoadingSpinner />
+    <section id="stats" class="flex justify-center items-center">
+        <div class="text-center">
+            <div class="mb-2">Loading...</div>
+            <LoadingSpinner />
+        </div>

121-362: Consider creating a reusable loading state component.

Both metrics and counters sections implement similar loading and error states. Consider extracting this pattern into a reusable component to improve maintainability and consistency.

Example implementation:

<!-- LoadingState.svelte -->
<script lang="ts">
  export let loading = false;
  export let error: Error | null = null;
  export let onRetry: () => void;
</script>

{#if loading}
  <div class="flex justify-center items-center">
    <div class="text-center">
      <div class="mb-2">Loading...</div>
      <LoadingSpinner />
    </div>
  </div>
{:else if error}
  <div class="flex justify-center items-center">
    <div class="text-center">
      <p class="text-red-500 mb-2">Failed to load data. Please try again.</p>
      <button class="btn variant-filled" on:click={onRetry}>
        Retry
      </button>
    </div>
  </div>
{:else}
  <slot />
{/if}

Usage:

- {#await data.stream.metrics}
-   <div class="col-span-3 lg:col-span-1">
-     <div>Refreshing data ..</div>
-     <LoadingSpinner />
-   </div>
- {:then metrics}
+ <LoadingState
+   loading={!data.stream.metrics}
+   error={metricsError}
+   onRetry={() => window.location.reload()}
+ >
    <section id="summary" class="grid grid-cols-6 gap-2">
      <!-- ... -->
    </section>
+ </LoadingState>
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d58af59 and ce031cf.

📒 Files selected for processing (1)
  • frontend/src/routes/(app)/(internal)/analytics/+page.svelte (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (8)
  • GitHub Check: startup-functional-test (3.12)
  • GitHub Check: startup-docker-compose-test
  • GitHub Check: enterprise-startup-functional-test (3.12)
  • GitHub Check: functional-tests (3.12, chromium)
  • GitHub Check: enterprise-functional-tests (3.12, chromium)
  • GitHub Check: enterprise-startup-docker-compose-test
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (1)
frontend/src/routes/(app)/(internal)/analytics/+page.svelte (1)

20-20: LGTM! Clean import of LoadingSpinner component.

</div>
</section>
{:catch error}
<div>Data load eror</div>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo in error message and enhance error handling.

The error message contains a typo and lacks proper handling:

-    <div>Data load eror</div>
+    <section id="stats" class="flex justify-center items-center">
+        <div class="text-center">
+            <p class="text-red-500 mb-2">Failed to load statistics. Please try again.</p>
+            <button class="btn variant-filled" on:click={() => window.location.reload()}>
+                Retry
+            </button>
+        </div>
+    </section>
📝 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
<div>Data load eror</div>
<section id="stats" class="flex justify-center items-center">
<div class="text-center">
<p class="text-red-500 mb-2">Failed to load statistics. Please try again.</p>
<button class="btn variant-filled" on:click={() => window.location.reload()}>
Retry
</button>
</div>
</section>

Comment on lines +306 to +307
<div class="col-span-3 lg:col-span-1">
<p class="text-red-500">Error loading metrics</p>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Enhance error handling with recovery options.

The error message should provide more context and a way to recover:

-    <div class="col-span-3 lg:col-span-1">
-        <p class="text-red-500">Error loading metrics</p>
+    <section id="summary" class="grid grid-cols-6 gap-2">
+        <div class="col-span-6 flex items-center justify-center">
+            <div class="text-center">
+                <p class="text-red-500 mb-2">Failed to load metrics. Please try again.</p>
+                <button class="btn variant-filled" on:click={() => window.location.reload()}>
+                    Retry
+                </button>
+            </div>
+        </div>
📝 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
<div class="col-span-3 lg:col-span-1">
<p class="text-red-500">Error loading metrics</p>
<section id="summary" class="grid grid-cols-6 gap-2">
<div class="col-span-6 flex items-center justify-center">
<div class="text-center">
<p class="text-red-500 mb-2">Failed to load metrics. Please try again.</p>
<button class="btn variant-filled" on:click={() => window.location.reload()}>
Retry
</button>
</div>
</div>

Copy link
Collaborator

@eric-intuitem eric-intuitem left a comment

Choose a reason for hiding this comment

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

LGTM

@ab-smith ab-smith merged commit f02055e into main Jan 28, 2025
18 checks passed
@ab-smith ab-smith deleted the progressive_loading_experiment branch January 28, 2025 23:51
@github-actions github-actions bot locked and limited conversation to collaborators Jan 28, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants