Skip to content

feat: Implement Project Analytics Dashboard (#41)#149

Open
sameezy667 wants to merge 1 commit intoStabilityNexus:mainfrom
sameezy667:feature/project-analytics-41
Open

feat: Implement Project Analytics Dashboard (#41)#149
sameezy667 wants to merge 1 commit intoStabilityNexus:mainfrom
sameezy667:feature/project-analytics-41

Conversation

@sameezy667
Copy link
Copy Markdown
Contributor

@sameezy667 sameezy667 commented Dec 13, 2025

  • Add comprehensive analytics service for metrics calculation
  • Create 3 chart components (Pie, Line, Bar) using native Canvas API
  • Build two-view analytics dashboard (Platform + Project details)
  • Implement time-series tracking with localStorage persistence
  • Add JSON and CSV export functionality
  • Maintain 100% client-side operation (no backend required)
  • Include complete documentation and user guides

Closes #41

Summary by CodeRabbit

  • New Features
    • Analytics dashboard with platform overview and detailed per-project views
    • Interactive charts visualizing funding progress, contributions, and project status
    • Export analytics data in JSON and CSV formats for reporting
    • Historical time-series tracking with automatic data persistence

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

- Add comprehensive analytics service for metrics calculation
- Create 3 chart components (Pie, Line, Bar) using native Canvas API
- Build two-view analytics dashboard (Platform + Project details)
- Implement time-series tracking with localStorage persistence
- Add JSON and CSV export functionality
- Maintain 100% client-side operation (no backend required)
- Include complete documentation and user guides

Closes StabilityNexus#41
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Dec 13, 2025

Walkthrough

Introduces a comprehensive client-side Analytics feature for the Bene platform, featuring data collection and metrics calculation via AnalyticsService, three Canvas-based chart components (PieChart, LineChart, BarChart), a dual-view Analytics dashboard with platform and project-level insights, localStorage-based time-series persistence, and JSON/CSV export capabilities.

Changes

Cohort / File(s) Summary
Core Analytics Service
src/lib/analytics/analytics-service.ts
New AnalyticsService class with static methods for computing project and platform metrics, managing localStorage persistence of analytics snapshots, generating time-series data, and exporting data as JSON or CSV. Exposes ProjectMetrics, ContributorMetrics, TimeSeriesData, and PlatformAnalytics interfaces.
Chart Components
src/lib/components/charts/PieChart.svelte, src/lib/components/charts/LineChart.svelte, src/lib/components/charts/BarChart.svelte
Three Canvas-based visualization components with configurable dimensions, labels, and legends. PieChart renders donut-style charts with optional legend; LineChart plots connected data points with grid; BarChart displays vertical bars with value labels. All support responsive sizing and theme-aware rendering.
Analytics Dashboard
src/routes/Analytics.svelte
New primary analytics route implementing a two-view dashboard: Platform Overview (project summaries, status distribution, metrics cards, export actions) and Project Details (funding progress, token metrics, time-series trends, detailed breakdown). Integrates chart components and navigation between views.
Navigation Integration
src/routes/App.svelte
Added Analytics tab to navigation (desktop and mobile), imports Analytics component, and wires projects and currentHeight props from application state.
Documentation
ANALYTICS_FEATURE.md, IMPLEMENTATION_SUMMARY.md, QUICK_START_ANALYTICS.md, ISSUE_41_COMPLETION.md
Four comprehensive markdown documents describing feature architecture, technical implementation, user quick-start guide with troubleshooting, and completion summary with requirements checklist.
Readme Update
README.md
Added Analytics section with feature overview, subsections for Platform Overview, Project Details, Data Export, and static-page compatibility, plus documentation links.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Analytics Dashboard
    participant AnalyticsService
    participant localStorage
    participant Chart Components
    
    User->>Analytics Dashboard: Navigate to Analytics
    activate Analytics Dashboard
    
    Analytics Dashboard->>AnalyticsService: calculatePlatformAnalytics(projects)
    activate AnalyticsService
    AnalyticsService->>AnalyticsService: Process all projects<br/>compute metrics
    AnalyticsService-->>Analytics Dashboard: PlatformAnalytics result
    deactivate AnalyticsService
    
    Analytics Dashboard->>Chart Components: Render Platform Overview
    activate Chart Components
    Chart Components-->>Analytics Dashboard: Charts rendered
    deactivate Chart Components
    
    User->>Analytics Dashboard: Click on project
    Analytics Dashboard->>AnalyticsService: calculateProjectMetrics(project)
    activate AnalyticsService
    AnalyticsService->>localStorage: Store snapshot
    AnalyticsService-->>Analytics Dashboard: ProjectMetrics + TimeSeriesData
    deactivate AnalyticsService
    
    Analytics Dashboard->>Chart Components: Render Project Details<br/>(PieChart, LineChart)
    activate Chart Components
    Chart Components-->>Analytics Dashboard: Charts rendered
    deactivate Chart Components
    
    User->>Analytics Dashboard: Click Export JSON
    Analytics Dashboard->>AnalyticsService: exportAsJSON(data)
    activate AnalyticsService
    AnalyticsService->>AnalyticsService: Generate Blob
    AnalyticsService-->>User: Download file
    deactivate AnalyticsService
    
    deactivate Analytics Dashboard
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Canvas rendering logic across three chart components: Each component implements distinct drawing routines with grid lines, labels, legends, and responsive scaling that require validation of coordinate calculations and visual accuracy.
  • AnalyticsService calculation methods: Multiple metric aggregation, status determination (active/successful/failed), and time-series generation logic that depend on timestamp vs. height comparisons and financial calculations.
  • Analytics.svelte dashboard complexity: Two-view system with reactive calculations, state management across projects, and integration with multiple chart and UI components; prop wiring and event handling chains.
  • localStorage persistence and snapshot management: History capping (max 1000), retrieval patterns, and data serialization/deserialization require careful validation.
  • Export functionality: JSON/CSV generation with proper escaping and filename handling; file download mechanics via Blob URLs.

Poem

🐰✨ A dashboard of numbers and charts so fine,
With canvas-drawn lines and pie slices divine!
From projects to metrics, the data now flows,
With localStorage wisdom that stores and bestows.
Export your insights with just one swift click—
Analytics magic, and oh-so-quick! 📊

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: Implement Project Analytics Dashboard (#41)' clearly summarizes the main change: adding a comprehensive analytics dashboard feature that directly aligns with the primary objective of the changeset.
Linked Issues check ✅ Passed All five objectives from issue #41 are successfully met: data collection via AnalyticsService [analytics-service.ts], visualization components PieChart/LineChart/BarChart [charts/], contributor analysis in Analytics dashboard [Analytics.svelte], time-series tracking with localStorage [analytics-service.ts], and JSON/CSV export functionality [analytics-service.ts, Analytics.svelte].
Out of Scope Changes check ✅ Passed All changes are directly scoped to implementing the analytics feature: new analytics service, chart components, dashboard, documentation, and navigation integration. No unrelated modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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: 4

🧹 Nitpick comments (8)
ANALYTICS_FEATURE.md (1)

131-146: Consider adding language identifiers to code blocks.

For better syntax highlighting and clarity, add language identifiers to the fenced code blocks (e.g., ```bash or ```plaintext).

Apply this pattern:

-```
+```plaintext
 Platform Overview → "📥 Export Data" button

</blockquote></details>
<details>
<summary>src/lib/components/charts/PieChart.svelte (1)</summary><blockquote>

`74-78`: **Consider theme-aware text colors.**

The percentage labels use hardcoded white text (`#ffffff`), which may have poor contrast on light themes. While the documentation claims "theme-aware" charts, the canvas text color doesn't adapt.



You could detect the theme and adjust text color accordingly, or use a semi-transparent approach that works on both themes. However, if the pie slices always use relatively dark colors, white text may be acceptable.

</blockquote></details>
<details>
<summary>src/routes/Analytics.svelte (4)</summary><blockquote>

`17-17`: **Use explicit typing instead of `any[]`.**

`timeSeriesData` is typed as `any[]`, losing type safety. Since `TimeSeriesData` is already imported from the analytics service, use it here.



```diff
-    let timeSeriesData: any[] = [];
+    let timeSeriesData: import("$lib/analytics/analytics-service").TimeSeriesData[] = [];

Or add TimeSeriesData to the existing import on line 4.


26-34: Consider throttling snapshot storage to avoid near-duplicate data points.

Every time selectedProject changes, a snapshot is stored immediately. If a user navigates back and forth between projects or re-selects the same project, the history accumulates many data points with nearly identical timestamps and values, degrading time-series data quality.

Consider adding a minimum interval (e.g., 5 minutes) or checking if the last snapshot's metrics differ meaningfully before storing:

     $: if (selectedProject) {
         selectedProjectMetrics = AnalyticsService.calculateProjectMetrics(selectedProject, currentHeight);
         timeSeriesData = AnalyticsService.generateTimeSeriesData(selectedProject.project_id);
         
         // Store current snapshot
-        if (selectedProjectMetrics) {
+        // Only store if enough time has passed since last snapshot
+        const lastSnapshot = timeSeriesData[timeSeriesData.length - 1];
+        const MIN_INTERVAL = 5 * 60 * 1000; // 5 minutes
+        if (selectedProjectMetrics && (!lastSnapshot || Date.now() - lastSnapshot.timestamp > MIN_INTERVAL)) {
             AnalyticsService.storeAnalyticsSnapshot(selectedProject.project_id, selectedProjectMetrics);
         }
     }

184-205: Metrics are recalculated on every render.

calculateProjectMetrics is called inline for each project in the {#each} block. This computation runs on every component re-render (e.g., when exporting data). Additionally, calculatePlatformAnalytics already iterates all projects and computes their metrics internally.

Consider caching the per-project metrics in a reactive variable to avoid redundant calculations:

$: projectMetricsMap = new Map(
    projects.map(p => [p.project_id, AnalyticsService.calculateProjectMetrics(p, currentHeight)])
);

Then reference projectMetricsMap.get(project.project_id) in the template.


1-94: Unused import: onMount.

The onMount import from Svelte (line 2) is not used anywhere in the component. Consider removing it to keep imports clean.

 <script lang="ts">
-    import { onMount } from "svelte";
     import { type Project } from "$lib/common/project";
src/lib/analytics/analytics-service.ts (2)

218-218: Handle potential localStorage quota error.

localStorage.setItem can throw a QuotaExceededError if the storage limit is reached. While the 1000-snapshot limit helps, accumulated data across many projects could still exceed quotas on some browsers.

-        localStorage.setItem(key, JSON.stringify(existing));
+        try {
+            localStorage.setItem(key, JSON.stringify(existing));
+        } catch (error) {
+            console.error("Failed to store analytics snapshot (quota exceeded?):", error);
+            // Optionally: clear older data or notify user
+        }

284-291: CSV export doesn't handle newlines in values.

The escaping logic handles commas and quotes, but strings containing newlines would break CSV row structure. While unlikely with current data, this could cause issues if project names/content include line breaks.

                     const value = row[header];
                     // Escape commas and quotes
-                    if (typeof value === 'string' && (value.includes(',') || value.includes('"'))) {
+                    if (typeof value === 'string' && (value.includes(',') || value.includes('"') || value.includes('\n'))) {
                         return `"${value.replace(/"/g, '""')}"`;
                     }
                     return value;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e1ac3b and 4a05b26.

📒 Files selected for processing (12)
  • ANALYTICS_FEATURE.md (1 hunks)
  • GIT_COMMIT_MESSAGE.md (1 hunks)
  • IMPLEMENTATION_SUMMARY.md (1 hunks)
  • ISSUE_41_COMPLETION.md (1 hunks)
  • QUICK_START_ANALYTICS.md (1 hunks)
  • README.md (1 hunks)
  • src/lib/analytics/analytics-service.ts (1 hunks)
  • src/lib/components/charts/BarChart.svelte (1 hunks)
  • src/lib/components/charts/LineChart.svelte (1 hunks)
  • src/lib/components/charts/PieChart.svelte (1 hunks)
  • src/routes/Analytics.svelte (1 hunks)
  • src/routes/App.svelte (4 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/lib/analytics/analytics-service.ts (2)
src/lib/common/project.ts (1)
  • Project (60-85)
src/lib/common/store.ts (1)
  • projects (14-17)
🪛 LanguageTool
ANALYTICS_FEATURE.md

[grammar] ~247-~247: Use a hyphen to join words.
Context: ...ytics features: 1. Maintain client-side only approach 2. Use localStorage for pe...

(QB_NEW_EN_HYPHEN)

🪛 markdownlint-cli2 (0.18.1)
QUICK_START_ANALYTICS.md

91-91: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


108-108: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

ANALYTICS_FEATURE.md

131-131: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


137-137: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


143-143: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


186-186: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


260-260: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

🔇 Additional comments (11)
ISSUE_41_COMPLETION.md (1)

1-259: LGTM! Comprehensive completion documentation.

The completion report is well-structured, provides clear visual representations, and thoroughly documents the delivered features. The statistics and metadata provide good context for the implementation scope.

README.md (1)

146-191: LGTM! Clear and comprehensive analytics documentation.

The Analytics Feature section is well-integrated into the README, provides a clear overview of capabilities, and properly links to detailed documentation files. The emphasis on client-side operation aligns with the platform's decentralized philosophy.

src/routes/App.svelte (2)

254-258: LGTM! Analytics navigation properly integrated.

The Analytics tab has been correctly added to both desktop and mobile navigation menus, maintaining consistency with existing navigation patterns.

Also applies to: 349-353


372-374: LGTM! Props correctly passed to Analytics component.

The data transformation from projectsStore.data (Map) to an array is correct, and the nullish coalescing for current_height properly handles the type conversion from number | null to number | undefined.

QUICK_START_ANALYTICS.md (1)

1-179: LGTM! Comprehensive and user-friendly guide.

The quick start guide is well-structured with clear instructions for developers, users, and project owners. The troubleshooting section addresses common issues effectively. Code examples are accurate and actionable.

src/lib/components/charts/PieChart.svelte (1)

33-83: LGTM! Solid pie chart implementation with good edge case handling.

The chart correctly handles empty data, zero totals, and division-by-zero scenarios. The math for angle calculations is accurate, and the 5% threshold for labels prevents visual clutter.

src/lib/components/charts/LineChart.svelte (1)

26-140: LGTM! Well-implemented line chart with good scaling and labeling.

The chart properly calculates min/max values, scales data appropriately, and uses smart label frequency to avoid crowding. Grid lines and axis labels enhance readability.

src/lib/components/charts/BarChart.svelte (1)

26-142: Good bar chart implementation with useful features.

The chart includes nice touches like gradient fills, value labels above bars, and smart label rotation for long text. The overall structure and rendering logic are solid.

GIT_COMMIT_MESSAGE.md (1)

1-114: Comprehensive commit documentation.

The commit message provides a thorough summary of the analytics feature implementation, covering all key aspects including changes, features, technical details, and testing status. This level of documentation aids future maintainability.

IMPLEMENTATION_SUMMARY.md (1)

1-228: Well-structured implementation documentation.

The implementation summary provides clear traceability to Issue #41 requirements, documents the architecture decisions, and includes a useful testing checklist. Good reference material for future maintenance.

src/lib/analytics/analytics-service.ts (1)

91-142: Well-structured analytics service.

The AnalyticsService provides a clean, stateless API for calculating metrics, managing persistence, and exporting data. The use of static methods is appropriate given the utility nature of the service, and the TypeScript interfaces provide good type safety.

Comment on lines +173 to +175
const averageProjectSuccess = projects.length > 0
? (successfulProjects / (successfulProjects + failedProjects)) * 100
: 0;
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 NaN from division by zero.

If all projects are currently active (none have ended), both successfulProjects and failedProjects will be 0, resulting in 0 / 0 = NaN.

         const averageProjectSuccess = projects.length > 0 
-            ? (successfulProjects / (successfulProjects + failedProjects)) * 100 
+            ? (successfulProjects + failedProjects > 0 
+                ? (successfulProjects / (successfulProjects + failedProjects)) * 100 
+                : 0)
             : 0;
📝 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
const averageProjectSuccess = projects.length > 0
? (successfulProjects / (successfulProjects + failedProjects)) * 100
: 0;
const averageProjectSuccess = projects.length > 0
? (successfulProjects + failedProjects > 0
? (successfulProjects / (successfulProjects + failedProjects)) * 100
: 0)
: 0;
🤖 Prompt for AI Agents
In src/lib/analytics/analytics-service.ts around lines 173 to 175, the
calculation can produce NaN when successfulProjects + failedProjects equals 0;
change the logic to compute a denominator variable (e.g., const totalEnded =
successfulProjects + failedProjects) and only perform the division when
totalEnded > 0, otherwise set averageProjectSuccess to 0 (or another safe
fallback); ensure the result uses the computed denominator and preserves the
percentage multiplication.

Comment on lines +65 to +91
data.forEach((item, index) => {
const x = padding.left + (chartWidth / data.length) * index + barSpacing / 2;
const barHeight = (item.value / maxValue) * chartHeight;
const y = padding.top + chartHeight - barHeight;

const color = item.color || defaultColor;

// Draw bar with gradient
const gradient = ctx!.createLinearGradient(x, y, x, y + barHeight);
gradient.addColorStop(0, color);
gradient.addColorStop(1, color + "aa");

ctx!.fillStyle = gradient;
ctx!.fillRect(x, y, barWidth, barHeight);

// Draw border
ctx!.strokeStyle = color;
ctx!.lineWidth = 2;
ctx!.strokeRect(x, y, barWidth, barHeight);

// Draw value on top of bar
ctx!.fillStyle = "#333";
ctx!.font = "bold 12px sans-serif";
ctx!.textAlign = "center";
ctx!.textBaseline = "bottom";
ctx!.fillText(item.value.toFixed(0), x + barWidth / 2, y - 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

Potential issues with color gradient and zero values.

Two concerns:

  1. Line 75: color + "aa" assumes the color is in hex format. If a color is provided in rgb(), rgba(), or hsl() format, the concatenation will produce an invalid color string.

  2. Line 67: If all data values are 0, maxValue will be 0, causing item.value / maxValue to be 0/0 = NaN, resulting in NaN for bar heights.

Consider these fixes:

 function drawChart() {
     if (!ctx || !data || data.length === 0) return;
     
     // ...
     
     const maxValue = Math.max(...data.map(d => d.value));
+    if (maxValue === 0) {
+        // Draw empty chart with message or just return
+        ctx.fillStyle = "#888";
+        ctx.font = "14px sans-serif";
+        ctx.textAlign = "center";
+        ctx.fillText("No data to display", width / 2, height / 2);
+        return;
+    }
     
     // ...
     
     // Draw bars
     data.forEach((item, index) => {
         // ...
         const color = item.color || defaultColor;
         
         // Draw bar with gradient
         const gradient = ctx!.createLinearGradient(x, y, x, y + barHeight);
         gradient.addColorStop(0, color);
-        gradient.addColorStop(1, color + "aa");
+        // Only add alpha if color is hex format
+        const colorWithAlpha = color.startsWith('#') ? color + "aa" : color;
+        gradient.addColorStop(1, colorWithAlpha);

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

Comment on lines +70 to +79
data.forEach((point, index) => {
const x = padding.left + (chartWidth / (data.length - 1)) * index;
const y = padding.top + chartHeight - ((point.value - minValue) / valueRange) * chartHeight;

if (index === 0) {
ctx!.moveTo(x, y);
} else {
ctx!.lineTo(x, y);
}
});
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 with single data point.

Lines 71 and 85 use (data.length - 1) as a divisor for calculating X coordinates. If the data array has only one element, this results in division by zero, producing NaN coordinates.

Add a guard or special handling for single data points:

 function drawChart() {
     if (!ctx || !data || data.length === 0) return;
+    
+    // Handle single data point
+    if (data.length === 1) {
+        const point = data[0];
+        const x = padding.left + chartWidth / 2;
+        const y = padding.top + chartHeight / 2;
+        
+        ctx.beginPath();
+        ctx.arc(x, y, 4, 0, Math.PI * 2);
+        ctx.fillStyle = color;
+        ctx.fill();
+        ctx.strokeStyle = "#fff";
+        ctx.lineWidth = 2;
+        ctx.stroke();
+        
+        // Draw label
+        ctx.fillStyle = "#888";
+        ctx.font = "11px sans-serif";
+        ctx.textAlign = "center";
+        ctx.fillText(point.label, x, padding.top + chartHeight + 10);
+        return;
+    }
     
     // Clear canvas
     ctx.clearRect(0, 0, width, height);

Also applies to: 84-95

🤖 Prompt for AI Agents
In src/lib/components/charts/LineChart.svelte around lines 70 to 95, the code
divides by (data.length - 1) when computing X coordinates which yields
division-by-zero for a single data point; compute a safe stepX first (e.g. stepX
= data.length > 1 ? chartWidth / (data.length - 1) : 0) and then compute x using
either padding.left + stepX * index for multi-point datasets or center the
single point (e.g. padding.left + chartWidth / 2) when stepX is 0; apply the
same safe stepX logic to the other block referenced (lines ~84–95) so no code
path divides by zero.

Comment thread src/routes/App.svelte
Comment on lines +14 to +16
projects as projectsStore,
} from "$lib/common/store";
import { fetchProjects } from "$lib/ergo/fetch";
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

🧩 Analysis chain

🏁 Script executed:

rg -n "fetchProjects" --type=svelte -A2 -B2

Repository: StabilityNexus/BenefactionPlatform-Ergo

Length of output: 113


🏁 Script executed:

rg -n "fetchProjects" src/routes/App.svelte

Repository: StabilityNexus/BenefactionPlatform-Ergo

Length of output: 138


🏁 Script executed:

rg -n "fetchProjects" --max-count=20

Repository: StabilityNexus/BenefactionPlatform-Ergo

Length of output: 1166


Remove unused fetchProjects import.

The fetchProjects import on line 16 is not used anywhere in this file and should be removed.

🤖 Prompt for AI Agents
In src/routes/App.svelte around lines 14 to 16, the import fetchProjects is
unused; remove the fetchProjects import from the import list (delete the "import
{ fetchProjects } from \"$lib/ergo/fetch\";" or remove fetchProjects from the
existing import statement) and ensure trailing commas/line breaks remain
consistent so the remaining imports compile correctly.

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.

Project Analytics

3 participants