diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1db9e2f..f5bb9c4 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -65,6 +65,28 @@ jobs:
- name: Lint code for consistent style
run: bin/rubocop -f github
+ frontend:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v6
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v5
+ with:
+ node-version: 22
+ cache: npm
+
+ - name: Install JavaScript dependencies
+ run: npm ci
+
+ - name: Run Svelte type checks
+ run: npm run check:svelte
+
+ - name: Run Svelte formatting checks
+ run: npm run format:svelte:check
+
zeitwerk:
runs-on: ubuntu-latest
diff --git a/.prettierrc.json b/.prettierrc.json
new file mode 100644
index 0000000..a4e08f4
--- /dev/null
+++ b/.prettierrc.json
@@ -0,0 +1,13 @@
+{
+ "plugins": ["prettier-plugin-svelte"],
+ "overrides": [
+ {
+ "files": "*.svelte",
+ "options": {
+ "parser": "svelte",
+ "tabWidth": 2,
+ "useTabs": false
+ }
+ }
+ ]
+}
diff --git a/app/assets/tailwind/application.css b/app/assets/tailwind/application.css
index f211a56..42610ce 100644
--- a/app/assets/tailwind/application.css
+++ b/app/assets/tailwind/application.css
@@ -3,7 +3,6 @@
@source "../../../node_modules/layerchart/dist/**/*.{svelte,js}";
@import "./main.css";
@import "./nav.css";
-@import "./filterable_dashboard.css";
body {
@apply flex min-h-screen;
@@ -291,4 +290,3 @@ html[data-theme="nord"] {
background-color: var(--color-primary);
opacity: 0.9;
}
-
diff --git a/app/assets/tailwind/filterable_dashboard.css b/app/assets/tailwind/filterable_dashboard.css
deleted file mode 100644
index 2030f56..0000000
--- a/app/assets/tailwind/filterable_dashboard.css
+++ /dev/null
@@ -1,12 +0,0 @@
-.container {
- @apply max-w-6xl mx-auto my-0 px-4 py-8 pb-0;
-}
-
-#filterable_dashboard_content.loading > div {
- @apply grayscale opacity-70 pointer-events-none transition-[filter] duration-200 ease-in-out;
-}
-
-.filter .option input[type="checkbox"]:checked::after {
- content: "";
- @apply absolute left-1 top-1 w-1 h-2 border-white border-solid rotate-45 border-0 border-r-2 border-b-2;
-}
diff --git a/app/controllers/api/v1/dashboard_stats_controller.rb b/app/controllers/api/v1/dashboard_stats_controller.rb
deleted file mode 100644
index a287a09..0000000
--- a/app/controllers/api/v1/dashboard_stats_controller.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-module Api
- module V1
- class DashboardStatsController < ApplicationController
- include DashboardData
-
- before_action :require_session_user!
-
- def show
- render json: {
- filterable_dashboard_data: filterable_dashboard_data,
- activity_graph: activity_graph_data,
- today_stats: today_stats_data
- }
- end
-
- private
-
- def require_session_user!
- render json: { error: "Unauthorized" }, status: :unauthorized unless current_user
- end
- end
- end
-end
diff --git a/app/controllers/static_pages_controller.rb b/app/controllers/static_pages_controller.rb
index 27da71b..4dea016 100644
--- a/app/controllers/static_pages_controller.rb
+++ b/app/controllers/static_pages_controller.rb
@@ -2,10 +2,6 @@ class StaticPagesController < InertiaController
include DashboardData
layout "inertia", only: :index
- before_action :ensure_current_user, only: %i[
- filterable_dashboard
- filterable_dashboard_content
- ]
def index
if current_user
@@ -108,32 +104,8 @@ class StaticPagesController < InertiaController
render partial: "streak"
end
- def filterable_dashboard
- load_dashboard_data
- %i[project language operating_system editor category].each do |f|
- instance_variable_set("@selected_#{f}", params[f]&.split(",") || [])
- end
- @selected_interval = params[:interval]
- @selected_from = params[:from]
- @selected_to = params[:to]
- render partial: "filterable_dashboard"
- end
-
- def filterable_dashboard_content
- load_dashboard_data
- render partial: "filterable_dashboard_content"
- end
-
private
- def load_dashboard_data
- filterable_dashboard_data.each { |k, v| instance_variable_set("@#{k}", v) }
- end
-
- def ensure_current_user
- redirect_to(root_path, alert: "You must be logged in to view this page") unless current_user
- end
-
def set_homepage_seo_content
@page_title = @og_title = @twitter_title = "Hackatime - See How Much You Code"
@meta_description = @og_description = @twitter_description = "Free and open source. Works with VS Code, JetBrains IDEs, vim, emacs, and 70+ other editors. Built and made free for teenagers by Hack Club."
@@ -151,16 +123,15 @@ class StaticPagesController < InertiaController
github_uid_blank: current_user&.github_uid.blank?,
github_auth_path: github_auth_path,
wakatime_setup_path: my_wakatime_setup_path,
- dashboard_stats_url: api_v1_dashboard_stats_path(
- interval: params[:interval],
- from: params[:from],
- to: params[:to],
- project: params[:project],
- language: params[:language],
- editor: params[:editor],
- operating_system: params[:operating_system],
- category: params[:category]
- )
+ dashboard_stats: InertiaRails.defer { dashboard_stats_payload }
+ }
+ end
+
+ def dashboard_stats_payload
+ {
+ filterable_dashboard_data: filterable_dashboard_data,
+ activity_graph: activity_graph_data,
+ today_stats: today_stats_data
}
end
diff --git a/app/javascript/components/Button.svelte b/app/javascript/components/Button.svelte
index ca8d966..88bb9df 100644
--- a/app/javascript/components/Button.svelte
+++ b/app/javascript/components/Button.svelte
@@ -1,13 +1,10 @@
{#if href}
-
-