From 0aa9e826aaf121504d281dd638c6220c6679d61f Mon Sep 17 00:00:00 2001 From: Elia Schito Date: Tue, 30 May 2023 15:17:24 +0200 Subject: [PATCH] Add a theme selector to the backend UI - Save the user preference alongside the system preference so that when the system switches the UI will follow along. - Use the session to store preferences so that we start the page with the correct theme(s). - Keep the current theme in the select tag up to date at page load and whenever the system changes. Co-Authored-By: piyushswain Co-Authored-By: Elia Schito Co-Authored-By: Massimiliano Lattanzio --- .../app/assets/javascripts/spree/backend.js | 1 + .../spree/backend/theme_selection.js | 36 +++++++++++++++++++ .../spree/admin/theme_controller.rb | 30 ++++++++++++++++ .../views/spree/admin/shared/_head.html.erb | 3 +- .../spree/admin/shared/_navigation.html.erb | 1 + .../admin/shared/_theme_selection.html.erb | 13 +++++++ backend/config/routes.rb | 1 + .../spree/admin/theme_controller_spec.rb | 32 +++++++++++++++++ core/config/locales/en.yml | 1 + 9 files changed, 117 insertions(+), 1 deletion(-) create mode 100644 backend/app/assets/javascripts/spree/backend/theme_selection.js create mode 100644 backend/app/controllers/spree/admin/theme_controller.rb create mode 100644 backend/app/views/spree/admin/shared/_theme_selection.html.erb create mode 100644 backend/spec/controllers/spree/admin/theme_controller_spec.rb diff --git a/backend/app/assets/javascripts/spree/backend.js b/backend/app/assets/javascripts/spree/backend.js index fdb073e0b95..50d821076b5 100644 --- a/backend/app/assets/javascripts/spree/backend.js +++ b/backend/app/assets/javascripts/spree/backend.js @@ -41,6 +41,7 @@ //= require spree/backend/images/index //= require spree/backend/images/upload //= require spree/backend/locale_selection +//= require spree/backend/theme_selection //= require spree/backend/navigation //= require spree/backend/option_type_autocomplete //= require spree/backend/option_value_picker diff --git a/backend/app/assets/javascripts/spree/backend/theme_selection.js b/backend/app/assets/javascripts/spree/backend/theme_selection.js new file mode 100644 index 00000000000..58a3892df16 --- /dev/null +++ b/backend/app/assets/javascripts/spree/backend/theme_selection.js @@ -0,0 +1,36 @@ +Spree.ready(() => { + const themeSelect = document.querySelector(".js-theme-selection") + const prefersDark = window.matchMedia("(prefers-color-scheme: dark)") + const lightTheme = themeSelect.dataset.selectedLightTheme + const darkTheme = themeSelect.dataset.selectedDarkTheme + const defaultTheme = themeSelect.dataset.defaultTheme + + const updateSelection = () => { + console.log({ + themeSelect, + prefersDark, + lightTheme, + darkTheme, + defaultTheme, + }) + themeSelect.value = (prefersDark.matches ? darkTheme : lightTheme) || defaultTheme + } + + const setTheme = () => { + Spree.ajax({ + type: "PUT", + dataType: "json", + url: Spree.pathFor("admin/theme/set"), + data: { + switch_to_theme: themeSelect.value, + system_theme: prefersDark.matches ? "dark" : "light", + }, + success: (data) => document.location.reload(), + }) + } + + updateSelection() + + prefersDark.addEventListener("change", updateSelection) + themeSelect.addEventListener("change", setTheme) +}) diff --git a/backend/app/controllers/spree/admin/theme_controller.rb b/backend/app/controllers/spree/admin/theme_controller.rb new file mode 100644 index 00000000000..06ea7879114 --- /dev/null +++ b/backend/app/controllers/spree/admin/theme_controller.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +module Spree + module Admin + class ThemeController < Spree::Admin::BaseController + skip_before_action :authorize_admin, only: [:set] + + def set + requested_theme = params[:switch_to_theme].presence + + # Avoid interpolating user content into the session key + system_theme = params[:system_theme].presence == "dark" ? "dark" : "light" + session_key = :"admin_#{system_theme}_theme" + + if theme_is_available?(requested_theme) + session[session_key] = requested_theme + head :ok + else + head :not_found + end + end + + private + + def theme_is_available?(theme) + theme && Spree::Backend::Config.themes.key?(theme.to_sym) + end + end + end +end diff --git a/backend/app/views/spree/admin/shared/_head.html.erb b/backend/app/views/spree/admin/shared/_head.html.erb index 1287add73a8..4bcfd5a455b 100644 --- a/backend/app/views/spree/admin/shared/_head.html.erb +++ b/backend/app/views/spree/admin/shared/_head.html.erb @@ -6,7 +6,8 @@ <%= admin_page_title %> <%= favicon_link_tag 'favicon.ico' %> -<%= stylesheet_link_tag Spree::Backend::Config.theme_path, media: 'all', data: {turbolinks_track: 'reload'} %> +<%= stylesheet_link_tag Spree::Backend::Config.theme_path(session[:admin_light_theme]), media: '(prefers-color-scheme: light)', data: {turbolinks_track: 'reload'} %> +<%= stylesheet_link_tag Spree::Backend::Config.theme_path(session[:admin_dark_theme]), media: '(prefers-color-scheme: dark)', data: {turbolinks_track: 'reload'} %> <%= javascript_include_tag 'spree/backend/all', data: {turbolinks_track: 'reload'} %> <%- if Rails.env.test? %> diff --git a/backend/app/views/spree/admin/shared/_navigation.html.erb b/backend/app/views/spree/admin/shared/_navigation.html.erb index 9f15ae2861c..17aaada1497 100644 --- a/backend/app/views/spree/admin/shared/_navigation.html.erb +++ b/backend/app/views/spree/admin/shared/_navigation.html.erb @@ -6,6 +6,7 @@ <%= button_tag class: 'btn fa fa-chevron-circle-left', id: 'admin-nav-toggle', type: :button do %> <%= t('spree.minimize_menu') %> <% end %> + <%= render partial: 'spree/admin/shared/theme_selection' %> <%= render partial: 'spree/admin/shared/locale_selection' %> <% if lookup_context.exists?('spree/admin/shared/_navigation_footer') %> <%= render partial: 'spree/admin/shared/navigation_footer' %> diff --git a/backend/app/views/spree/admin/shared/_theme_selection.html.erb b/backend/app/views/spree/admin/shared/_theme_selection.html.erb new file mode 100644 index 00000000000..7ff5db8a6a1 --- /dev/null +++ b/backend/app/views/spree/admin/shared/_theme_selection.html.erb @@ -0,0 +1,13 @@ + diff --git a/backend/config/routes.rb b/backend/config/routes.rb index f8c70640e61..343d1bc028e 100644 --- a/backend/config/routes.rb +++ b/backend/config/routes.rb @@ -6,6 +6,7 @@ get '/search/products', to: "search#products", as: :search_products put '/locale/set', to: 'locale#set', defaults: { format: :json }, as: :set_locale + put '/theme/set', to: 'theme#set', defaults: { format: :json }, as: :set_theme resources :dashboards, only: [] do collection do diff --git a/backend/spec/controllers/spree/admin/theme_controller_spec.rb b/backend/spec/controllers/spree/admin/theme_controller_spec.rb new file mode 100644 index 00000000000..503919f8180 --- /dev/null +++ b/backend/spec/controllers/spree/admin/theme_controller_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Spree::Admin::ThemeController, type: :controller do + stub_authorization! + + it 'sets the theme in a different session key for each system theme' do + stub_spree_preferences(Spree::Backend::Config, themes: { foo: 'foo-path', bar: 'bar-path' }) + + get :set, params: { switch_to_theme: 'foo', system_theme: 'light', format: :json } + + expect(session[:admin_light_theme]).to eq('foo') + expect(session[:admin_dark_theme]).to eq(nil) + expect(response).to have_http_status(:ok) + + get :set, params: { switch_to_theme: 'bar', system_theme: 'dark', format: :json } + expect(session[:admin_light_theme]).to eq('foo') + expect(session[:admin_dark_theme]).to eq('bar') + expect(response).to have_http_status(:ok) + end + + it 'responds with "not found" for a missing theme' do + stub_spree_preferences(Spree::Backend::Config, themes: { foo: 'foo-path' }) + + get :set, params: { switch_to_theme: 'bar', system_theme: 'dark', format: :json } + + expect(session[:admin_light_theme]).to eq(nil) + expect(session[:admin_dark_theme]).to eq(nil) + expect(response).to have_http_status(:not_found) + end +end diff --git a/core/config/locales/en.yml b/core/config/locales/en.yml index 64de0cbb2b1..6e89beacc3d 100644 --- a/core/config/locales/en.yml +++ b/core/config/locales/en.yml @@ -1135,6 +1135,7 @@ en: choose_a_taxon_to_sort_products_for: Choose a taxon to sort products for choose_currency: Choose Currency choose_dashboard_locale: Choose Dashboard Locale + choose_dashboard_theme: Choose Dashboard Theme choose_location: Choose Location choose_promotion_action: Choose Action choose_promotion_rule: Choose Rule