Skip to content

Commit

Permalink
Add Dark Mode color theme for Dashboard (#1031)
Browse files Browse the repository at this point in the history
  • Loading branch information
bensheldon committed Aug 6, 2023
1 parent 5f841e4 commit a8767bb
Show file tree
Hide file tree
Showing 30 changed files with 214 additions and 44 deletions.
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ GEM
msgpack (>= 0.4.3)
optimist (>= 3.0.0)
regexp_parser (2.8.1)
rexml (3.2.5)
rexml (3.2.6)
rspec-core (3.12.2)
rspec-support (~> 3.12.0)
rspec-expectations (3.12.3)
Expand Down Expand Up @@ -396,7 +396,7 @@ GEM
sawyer (0.9.2)
addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3)
selenium-webdriver (4.10.0)
selenium-webdriver (4.11.0)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
Expand Down
2 changes: 2 additions & 0 deletions app/frontend/good_job/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ import setupPopovers from "popovers";
import LivePoll from "live_poll";

import { Application } from "stimulus";
import ThemeController from "theme_controller";
window.Stimulus = Application.start();
Stimulus.register("theme", ThemeController)

documentReady(function() {
renderCharts();
Expand Down
44 changes: 44 additions & 0 deletions app/frontend/good_job/modules/theme_controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// hello_controller.js
import { Controller } from "stimulus"
export default class extends Controller {
static targets = [ "dropdown", "button" ]

connect() {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
const theme = localStorage.getItem('good_job-theme');
if (!["light", "dark"].includes(theme)) {
this.setTheme(this.autoTheme());
}
});

this.setTheme(this.getStoredTheme() || 'light');
}

change(event) {
const theme = event.params.value;
localStorage.setItem('good_job-theme', theme);
this.setTheme(theme);
}

setTheme(theme) {
document.documentElement.setAttribute('data-bs-theme', theme === 'auto' ? this.autoTheme() : theme);

this.buttonTargets.forEach((button) => {
button.classList.remove('active');
if (button.dataset.themeValueParam === theme) {
button.classList.add('active');
}
});

const svg = this.buttonTargets.filter(b => b.matches(".active"))[0]?.querySelector('svg');
this.dropdownTarget.querySelector('svg').outerHTML = svg.outerHTML;
}

getStoredTheme() {
return localStorage.getItem('good_job-theme');
}

autoTheme() {
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
}
}

Large diffs are not rendered by default.

7 changes: 3 additions & 4 deletions app/frontend/good_job/vendor/bootstrap/bootstrap.min.css

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions app/views/good_job/batches/_jobs.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="my-3 card" data-gj-poll-replace id="jobs-table">
<div class="list-group list-group-flush text-nowrap table-jobs" role="table">
<header class="list-group-item bg-light">
<header class="list-group-item bg-body-tertiary">
<div class="row small text-muted text-uppercase align-items-center">
<div class="col-4"><%=t "good_job.models.batch.jobs" %></div>
<div class="d-none d-lg-block col-lg-1 text-lg-center"><%=t "good_job.models.job.queue" %></div>
Expand All @@ -27,7 +27,7 @@
</div>
<div class="col-4 col-lg-1 text-lg-center">
<div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.job.queue" %></div>
<span class="badge bg-primary bg-opacity-25 text-dark font-monospace"><%= job.queue_name %></span>
<span class="badge bg-primary text-dark font-monospace"><%= job.queue_name %></span>
</div>
<div class="col-4 col-lg-1 text-lg-end">
<div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.job.priority" %>Priority</div>
Expand All @@ -43,7 +43,7 @@
bs_content: job.recent_error
} %>
<% else %>
<span class="badge bg-secondary bg-opacity-50 rounded-pill"><%= job.executions_count %></span>
<span class="badge bg-secondary rounded-pill"><%= job.executions_count %></span>
<% end %>
</div>
<div class="mt-3 mt-lg-0 col d-flex gap-3 align-items-center justify-content-end">
Expand Down
2 changes: 1 addition & 1 deletion app/views/good_job/batches/_table.erb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div class="my-3 card" data-gj-poll-replace id="batches-table">
<div class="list-group list-group-flush text-nowrap table-batches" role="table">
<header class="list-group-item bg-light">
<header class="list-group-item bg-body-tertiary">
<div class="row small text-muted text-uppercase align-items-center">
<div class="col-4"><%=t "good_job.models.batch.name" %></div>
<div class="col-lg-1 d-none d-lg-block"><%=t "good_job.models.batch.created" %></div>
Expand Down
2 changes: 1 addition & 1 deletion app/views/good_job/batches/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

<div class="my-4">
<h5><%= t ".attributes" %></h5>
<div class="bg-dark text-light p-3 rounded">
<div class="bg-dark text-secondary p-3 rounded">
<%= tag.pre JSON.pretty_generate @batch.display_attributes, class: 'text-wrap text-break' %>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions app/views/good_job/cron_entries/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<div class="card my-3">
<div class="list-group list-group-flush text-nowrap" role="table">
<header class="list-group-item bg-light">
<header class="list-group-item body-secondary">
<div class="row small text-muted text-uppercase align-items-center">
<div class="col-12 col-lg-2"></div>
<div class="col-6 col-lg-2 d-none d-lg-block"><%= t "good_job.models.cron.class" %></div>
Expand Down Expand Up @@ -68,7 +68,7 @@
</div>
</div>
</div>
<%= tag.div id: dom_id(cron_entry, 'properties'), class: "collapse cron-entry-properties list-group-item collapse small bg-dark text-light" do %>
<%= tag.div id: dom_id(cron_entry, 'properties'), class: "collapse cron-entry-properties list-group-item collapse small bg-dark text-secondary" do %>
<%= tag.pre JSON.pretty_generate(cron_entry.display_properties) %>
<% end %>
<% end %>
Expand Down
4 changes: 2 additions & 2 deletions app/views/good_job/jobs/_executions.erb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<%= tag.div id: dom_id(execution), class: "list-group-item py-3" do %>
<div class="row align-items-center text-nowrap">
<div class="col-md-5 d-flex gap-2">
<%= tag.span execution.number, class: "badge bg-secondary bg-opacity-50 rounded-pill" %>
<%= tag.span execution.number, class: "badge bg-secondary rounded-pill" %>
<%= tag.code link_to(execution.id, "##{dom_id(execution)}", class: "text-muted text-decoration-none small") %>
</div>
<div class="col-md-2 small">
Expand Down Expand Up @@ -36,7 +36,7 @@
<% if execution.error %>
<div class="mt-3 small">
<strong class="small"><%=t "good_job.shared.error" %>:</strong>
<code class="text-wrap text-break m-0 text-black"><%= execution.error %></code>
<code class="text-wrap text-break m-0 text-secondary-emphasis"><%= execution.error %></code>
</div>
<% end %>
<% end %>
Expand Down
19 changes: 10 additions & 9 deletions app/views/good_job/jobs/_table.erb
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
<%= form_with(url: mass_update_jobs_path(filter.to_params), method: :put, local: true, data: { "checkbox-toggle": "job_ids" }) do |form| %>
<div class="my-3 card" data-gj-poll-replace id="jobs-table">
<div class="list-group list-group-flush text-nowrap table-jobs" role="table">
<header class="list-group-item bg-light">
<header class="list-group-item bg-body-tertiary">
<div class="row small text-muted text-uppercase align-items-center">
<div class="col-lg-4 d-flex gap-2 flex-wrap">
<%= label_tag('toggle_job_ids', t(".toggle_all_jobs"), class: "visually-hidden") %>
<%= check_box_tag('toggle_job_ids', "1", false, data: { "checkbox-toggle-all": "job_ids" }) %>
<div class="form-check d-flex flex-row px-0 mb-0">
<%= check_box_tag('toggle_job_ids', "1", false, data: { "checkbox-toggle-all": "job_ids" }) %>
<%= label_tag('toggle_job_ids', t(".toggle_all_jobs"), class: "visually-hidden") %>
</div>
<%= form.button type: 'submit', name: 'mass_action', value: 'reschedule', class: 'ms-1 btn btn-sm btn-outline-secondary', title: t(".actions.reschedule_all"), data: { confirm: t(".actions.confirm_reschedule_all"), disable: true } do %>
<span class="me-1"><%= render_icon "skip_forward" %></span> <%=t "good_job.actions.reschedule" %>
<% end %>
Expand All @@ -22,13 +23,13 @@
<button id="destroy-dropdown-toggle" type="button" class="btn btn-sm btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden"><%=t ".toggle_actions" %></span>
</button>
<div class="dropdown-menu" aria-labelledby="destroy-dropdown-toggle">
<ul class="dropdown-menu" aria-labelledby="destroy-dropdown-toggle">
<li>
<%= form.button type: 'submit', name: 'mass_action', value: 'destroy', class: 'btn', title: t(".actions.destroy_all"), data: { confirm: t(".actions.confirm_destroy_all"), disable: true } do %>
<span class="me-1"><%= render_icon "trash" %></span> <%=t "good_job.actions.destroy" %>
<% end %>
</li>
</div>
</ul>
</div>

</div>
Expand Down Expand Up @@ -68,14 +69,14 @@
<% if job.error %>
<div class="mt-1 small">
<strong class="small"><%=t "good_job.shared.error" %>:</strong>
<code class="text-wrap text-break m-0 text-black"><%= job.error %></code>
<code class="text-wrap text-break m-0 text-secondary-emphasis"><%= job.error %></code>
</div>
<% end %>
</div>
</div>
<div class="col-4 col-lg-1 text-lg-center">
<div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.job.queue" %></div>
<span class="badge bg-primary bg-opacity-25 text-dark font-monospace"><%= job.queue_name %></span>
<span class="badge bg-primary font-monospace"><%= job.queue_name %></span>
</div>
<div class="col-4 col-lg-1 text-lg-end">
<div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.job.priority" %></div>
Expand All @@ -91,7 +92,7 @@
bs_content: job.display_error,
} %>
<% else %>
<span class="badge bg-secondary bg-opacity-50 rounded-pill"><%= job.executions_count %></span>
<span class="badge bg-secondary rounded-pill"><%= job.executions_count %></span>
<% end %>
</div>
<div class="mt-3 mt-lg-0 col">
Expand Down
4 changes: 2 additions & 2 deletions app/views/good_job/jobs/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<li class="breadcrumb-item active" aria-current="page">
<%= tag.code @job.id, class: "text-muted" %>
<% if @job.discrete? %>
<span class="badge bg-info text-dark">Discrete</span>
<span class="badge bg-info">Discrete</span>
<% end %>
</li>
</ol>
Expand All @@ -17,7 +17,7 @@
</div>
<div class="col-6 col-md-2">
<div class="small text-muted text-uppercase"><%= t "good_job.models.job.queue" %></div>
<div class="badge bg-primary bg-opacity-25 text-dark font-monospace my-2">
<div class="badge bg-primary font-monospace my-2">
<%= tag.strong @job.queue_name %>
</div>
</div>
Expand Down
6 changes: 3 additions & 3 deletions app/views/good_job/processes/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<div class="card my-3" data-live-poll-region="processes">
<div class="list-group list-group-flush text-nowrap" role="table">
<header class="list-group-item bg-light">
<header class="list-group-item bg-body-tertiary">
<div class="row small text-muted text-uppercase align-items-center">
<div class="col"><%= t ".process" %></div>
<div class="col"><%= t ".schedulers" %></div>
Expand Down Expand Up @@ -34,9 +34,9 @@
<% end %>
<div>
<span class="text-muted small">PID</span>
<span class="badge rounded-pill bg-light text-dark"><%= process.state["pid"] %></span>
<span class="badge rounded-pill bg-body-secondary text-secondary"><%= process.state["pid"] %></span>
<span class="text-muted small">@</span>
<span class="badge rounded-pill bg-light text-dark"><%= process.state["hostname"] %></span>
<span class="badge rounded-pill bg-body-secondary text-secondary"><%= process.state["hostname"] %></span>
</div>
</div>
<div class="col">
Expand Down
2 changes: 1 addition & 1 deletion app/views/good_job/shared/_footer.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<footer class="footer mt-auto py-3 bg-light border-top text-muted small" id="footer" data-live-poll-region="footer">
<footer class="footer mt-auto py-3 bg-body-tertiary border-top text-muted small" id="footer" data-live-poll-region="footer">
<div class="container-fluid">
<div class="row">
<div class="col-6">
Expand Down
57 changes: 50 additions & 7 deletions app/views/good_job/shared/_navbar.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<nav class="navbar navbar-expand-lg navbar-light border-bottom bg-white sticky-top shadow-sm">
<nav class="navbar navbar-expand-lg border-bottom bg-body sticky-top shadow-sm">
<div class="container-fluid">
<%= link_to t(".name"), root_path, class: "navbar-brand mb-0 h1" %>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
Expand Down Expand Up @@ -36,13 +36,24 @@
<% end %>
</li>
</ul>
<div class="nav-item pe-2">
<label>
<%= check_box_tag "live_poll", params.fetch("poll", 30), params[:poll].present? %>
<%= t(".live_poll") %>
</label>
</div>

<hr class="d-lg-none text-secondary">

<ul class="navbar-nav">
<li class="nav-item d-flex flex-column justify-content-center">
<div class="form-check form-switch m-0">
<%= check_box_tag "live_poll", params.fetch("poll", 30), params[:poll].present?, role: "switch", class: "form-check-input" %>
<label class="form-check-label navbar-text p-0" for="live_poll">
<%= t(".live_poll") %>
</label>
</div>
</li>

<li class="nav-item py-2 py-lg-1 col-12 col-lg-auto">
<div class="vr d-none d-lg-flex h-100 mx-lg-2 text-secondary"></div>
<hr class="d-lg-none my-2 text-secondary">
</li>

<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" type="button" id="localeOptions" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<%= I18n.locale %>
Expand All @@ -55,6 +66,38 @@
<% end %>
</ul>
</li>

<li class="nav-item py-2 py-lg-1 col-12 col-lg-auto">
<div class="vr d-none d-lg-flex h-100 mx-lg-2 text-secondary"></div>
<hr class="d-lg-none my-2 text-secondary">
</li>

<li class="nav-item dropdown" data-controller="theme">
<button class="nav-link dropdown-toggle" data-theme-target="dropdown" type="button" aria-expanded="false" data-bs-toggle="dropdown" data-bs-display="static" aria-label="Toggle theme">
<%= render_icon "circle_half" %>
<span class="visually-hidden"><%= t(".theme.theme") %></span>
</button>
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="bd-theme-text">
<li>
<button type="button" class="dropdown-item" data-theme-target="button" data-theme-value-param="light" data-action="theme#change" aria-pressed="true">
<%= render_icon "sun_fill" %>
<%= t(".theme.light") %>
</button>
</li>
<li>
<button type="button" class="dropdown-item" data-theme-target="button" data-theme-value-param="dark" data-action="theme#change" aria-pressed="false">
<%= render_icon "moon_stars_fill" %>
<%= t(".theme.dark") %>
</button>
</li>
<li>
<button type="button" class="dropdown-item btn btn-link" data-theme-target="button" data-theme-value-param="auto" data-action="theme#change" aria-pressed="false">
<%= render_icon "circle_half" %>
<%= t(".theme.auto") %>
</button>
</li>
</ul>
</li>
</ul>
</div>
</div>
Expand Down
4 changes: 4 additions & 0 deletions app/views/good_job/shared/icons/_circle_half.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<!-- https://icons.getbootstrap.com/icons/circle-half/ -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-circle-half" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 0 8 1v14zm0 1A8 8 0 1 1 8 0a8 8 0 0 1 0 16z" />
</svg>
5 changes: 5 additions & 0 deletions app/views/good_job/shared/icons/_moon_stars_fill.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<!-- https://icons.getbootstrap.com/icons/moon-stars-fill/ -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-moon-stars-fill" viewBox="0 0 16 16">
<path d="M6 .278a.768.768 0 0 1 .08.858 7.208 7.208 0 0 0-.878 3.46c0 4.021 3.278 7.277 7.318 7.277.527 0 1.04-.055 1.533-.16a.787.787 0 0 1 .81.316.733.733 0 0 1-.031.893A8.349 8.349 0 0 1 8.344 16C3.734 16 0 12.286 0 7.71 0 4.266 2.114 1.312 5.124.06A.752.752 0 0 1 6 .278z" />
<path d="M10.794 3.148a.217.217 0 0 1 .412 0l.387 1.162c.173.518.579.924 1.097 1.097l1.162.387a.217.217 0 0 1 0 .412l-1.162.387a1.734 1.734 0 0 0-1.097 1.097l-.387 1.162a.217.217 0 0 1-.412 0l-.387-1.162A1.734 1.734 0 0 0 9.31 6.593l-1.162-.387a.217.217 0 0 1 0-.412l1.162-.387a1.734 1.734 0 0 0 1.097-1.097l.387-1.162zM13.863.099a.145.145 0 0 1 .274 0l.258.774c.115.346.386.617.732.732l.774.258a.145.145 0 0 1 0 .274l-.774.258a1.156 1.156 0 0 0-.732.732l-.258.774a.145.145 0 0 1-.274 0l-.258-.774a1.156 1.156 0 0 0-.732-.732l-.774-.258a.145.145 0 0 1 0-.274l.774-.258c.346-.115.617-.386.732-.732L13.863.1z" />
</svg>
4 changes: 4 additions & 0 deletions app/views/good_job/shared/icons/_sun_fill.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<!-- https://icons.getbootstrap.com/icons/sun-fill/ -->
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-sun-fill" viewBox="0 0 16 16">
<path d="M8 12a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM8 0a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 0zm0 13a.5.5 0 0 1 .5.5v2a.5.5 0 0 1-1 0v-2A.5.5 0 0 1 8 13zm8-5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2a.5.5 0 0 1 .5.5zM3 8a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1 0-1h2A.5.5 0 0 1 3 8zm10.657-5.657a.5.5 0 0 1 0 .707l-1.414 1.415a.5.5 0 1 1-.707-.708l1.414-1.414a.5.5 0 0 1 .707 0zm-9.193 9.193a.5.5 0 0 1 0 .707L3.05 13.657a.5.5 0 0 1-.707-.707l1.414-1.414a.5.5 0 0 1 .707 0zm9.193 2.121a.5.5 0 0 1-.707 0l-1.414-1.414a.5.5 0 0 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .707zM4.464 4.465a.5.5 0 0 1-.707 0L2.343 3.05a.5.5 0 1 1 .707-.707l1.414 1.414a.5.5 0 0 1 0 .708z" />
</svg>
10 changes: 9 additions & 1 deletion app/views/layouts/good_job/application.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="<%= I18n.locale %>">
<html lang="<%= I18n.locale %>" data-bs-theme="auto">
<head>
<title>Good Job Dashboard</title>
<meta charset="utf-8">
Expand All @@ -19,6 +19,14 @@
<% importmaps = GoodJob::FrontendsController.js_modules.keys.index_with { |module_name| frontend_module_path(module_name, format: :js, locale: nil, v: GoodJob::VERSION) } %>
<%= tag.script({ imports: importmaps }.to_json.html_safe, type: "importmap", nonce: content_security_policy_nonce) %>
<%= tag.script "", type: "module", nonce: content_security_policy_nonce do %> import "application"; <% end %>
<%= tag.script "", nonce: content_security_policy_nonce do %>
// Ensure theme is updated before dom loads to avoid flash of style
let theme = localStorage.getItem('good_job-theme');
if (!["light", "dark"].includes(theme)) {
theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
document.documentElement.setAttribute('data-bs-theme', theme);
<% end %>
</head>
<body>
<div class="d-flex flex-column min-vh-100">
Expand Down
5 changes: 5 additions & 0 deletions config/locales/de.yml
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ de:
live_poll: Live Poll
name: "GoodJob 👍"
processes: Prozesse
theme:
auto: Auto
dark: Dunkel
light: Licht
theme: Thema
status:
discarded: Ausrangiert
queued: In der Warteschlange
Expand Down
5 changes: 5 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,11 @@ en:
live_poll: Live Poll
name: "GoodJob 👍"
processes: Processes
theme:
auto: Auto
dark: Dark
light: Light
theme: Theme
status:
discarded: Discarded
queued: Queued
Expand Down
Loading

0 comments on commit a8767bb

Please sign in to comment.