From 2293751fe25682032d3b8404756e1227184bbcae Mon Sep 17 00:00:00 2001 From: david Date: Tue, 12 Nov 2024 22:43:59 +0100 Subject: [PATCH 01/46] Add screenshots to elements --- Gemfile | 1 + Gemfile.lock | 14 +++ app/controllers/elements_controller.rb | 2 +- app/models/element.rb | 4 + app/views/elements/_element.html.erb | 17 ++- app/views/elements/_element.odt.erb | 3 + app/views/elements/_form.html.erb | 7 -- app/views/elements/_form.html.slim | 9 ++ app/views/elements/update.turbo_stream.slim | 3 +- app/views/exports/show.html.slim | 2 + bin/jobs | 6 + config/database.yml | 18 ++- config/environments/development.rb | 4 +- config/environments/production.rb | 4 +- config/puma.rb | 1 + config/queue.yml | 18 +++ config/recurring.yml | 10 ++ db/queue_schema.rb | 129 ++++++++++++++++++++ 18 files changed, 232 insertions(+), 20 deletions(-) delete mode 100644 app/views/elements/_form.html.erb create mode 100644 app/views/elements/_form.html.slim create mode 100755 bin/jobs create mode 100644 config/queue.yml create mode 100644 config/recurring.yml create mode 100644 db/queue_schema.rb diff --git a/Gemfile b/Gemfile index 4819425..7268a2b 100644 --- a/Gemfile +++ b/Gemfile @@ -58,6 +58,7 @@ gem "prawn-markup" gem "prawn-rails" gem "sablon" gem "slim" +gem "solid_queue" group :development, :test do # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem diff --git a/Gemfile.lock b/Gemfile.lock index be0da77..c9d597d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -116,12 +116,17 @@ GEM reline (>= 0.3.8) drb (2.2.1) erubi (1.13.0) + et-orbi (1.2.11) + tzinfo ffi (1.17.0-aarch64-linux-gnu) ffi (1.17.0-arm-linux-gnu) ffi (1.17.0-arm64-darwin) ffi (1.17.0-x86-linux-gnu) ffi (1.17.0-x86_64-darwin) ffi (1.17.0-x86_64-linux-gnu) + fugit (1.11.1) + et-orbi (~> 1, >= 1.2.11) + raabro (~> 1.4) globalid (1.2.1) activesupport (>= 6.1) hpricot (0.8.6) @@ -226,6 +231,7 @@ GEM public_suffix (6.0.0) puma (6.4.2) nio4r (~> 2.0) + raabro (1.4.0) racc (1.8.0) rack (3.1.7) rack-session (2.0.0) @@ -330,6 +336,13 @@ GEM slim (5.2.1) temple (~> 0.10.0) tilt (>= 2.1.0) + solid_queue (1.0.1) + activejob (>= 7.1) + activerecord (>= 7.1) + concurrent-ruby (>= 1.3.1) + fugit (~> 1.11.0) + railties (>= 7.1) + thor (~> 1.3.1) sorbet-runtime (0.5.11625) sqlite3 (2.2.0-aarch64-linux-gnu) sqlite3 (2.2.0-arm-linux-gnu) @@ -409,6 +422,7 @@ DEPENDENCIES sablon selenium-webdriver slim + solid_queue sqlite3 (>= 2.1) stimulus-rails turbo-rails diff --git a/app/controllers/elements_controller.rb b/app/controllers/elements_controller.rb index 152c904..abfd931 100644 --- a/app/controllers/elements_controller.rb +++ b/app/controllers/elements_controller.rb @@ -87,6 +87,6 @@ class ElementsController < ApplicationController # Only allow a list of trusted parameters through. def element_params - params.require(:element).permit(:page_id, :title, :description, :position) + params.require(:element).permit(:page_id, :title, :description, :position, :screenshot) end end diff --git a/app/models/element.rb b/app/models/element.rb index b46e492..e0b7ddf 100644 --- a/app/models/element.rb +++ b/app/models/element.rb @@ -11,6 +11,10 @@ class Element < ApplicationRecord before_validation :set_position before_update :update_positions, if: :position_changed? + has_one_attached :screenshot do |attachable| + attachable.variant :thumbnail, resize_to_limit: [ 200, 200 ] + end + # Calculate actual conformity level: # - if a success_criterion has result :failed -> the confirmity_level # of that success_criterion is not reached. diff --git a/app/views/elements/_element.html.erb b/app/views/elements/_element.html.erb index 346648f..a465f6c 100644 --- a/app/views/elements/_element.html.erb +++ b/app/views/elements/_element.html.erb @@ -17,11 +17,18 @@ <% end %> - <% if element.description %> -
- <%= element.description %> -
- <% end %> + +
+ <% if element.description %> +
+ <%= element.description %> +
+ <% end %> + <%= safe_display(element.screenshot) do %> + <%= image_tag(_1.variant(:thumbnail), class: "img-fluid", alt: "Screenshot des getesteten Elements") %> + <% end %> +
+

diff --git a/app/views/elements/_element.odt.erb b/app/views/elements/_element.odt.erb index bf1ae9e..fac3639 100644 --- a/app/views/elements/_element.odt.erb +++ b/app/views/elements/_element.odt.erb @@ -7,6 +7,9 @@ <%= element.description_html %> + <%= image_tag(element.screenshot.preview(:thumbnail)) %> + <%= image_tag(element.screenshot) %> + <% element.success_criteria.each do |sc| %> <%= render sc %> <% end %> diff --git a/app/views/elements/_form.html.erb b/app/views/elements/_form.html.erb deleted file mode 100644 index 1795bfc..0000000 --- a/app/views/elements/_form.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -<%= bootstrap_form_with(model: element.persisted? ? element : [:page, element], class: "mb-3") do |form| %> - <%= form.hidden_field :page_id %> - <%= form.text_field :title %> - <%= form.rich_text_area :description %> - <%= form.submit class: "btn btn-primary" %> - <%= link_to("Abbrechen", element.persisted? ? element : element.report, class: "btn btn-outline-secondary") %> -<% end %> diff --git a/app/views/elements/_form.html.slim b/app/views/elements/_form.html.slim new file mode 100644 index 0000000..4feb80d --- /dev/null +++ b/app/views/elements/_form.html.slim @@ -0,0 +1,9 @@ += bootstrap_form_with(model: element.persisted? ? element : [:page, element], class: "mb-3") do |form| + = form.hidden_field :page_id + = form.text_field :title + = form.rich_text_area :description + = form.file_field :screenshot + - if element.persisted? + = safe_display(element.screenshot) { tag.div(link_to(_1.filename.to_s, _1), class: "mb-3") } + = form.submit class: "btn btn-primary" + = link_to("Abbrechen", element.persisted? ? element : element.report, class: "btn btn-outline-secondary") diff --git a/app/views/elements/update.turbo_stream.slim b/app/views/elements/update.turbo_stream.slim index e6b478c..64c6489 100644 --- a/app/views/elements/update.turbo_stream.slim +++ b/app/views/elements/update.turbo_stream.slim @@ -4,4 +4,5 @@ / - element.success_criteria.each do |sc| / = turbo_stream.update dom_id(sc, :title), "#{sc.page.position}.#{sc.element.position}.#{sc.position} #{sc.title}" - element.success_criteria.each do |sc| - = turbo_stream.update dom_id(sc, :position), "#{sc.page.position}.#{sc.element.position}.#{sc.position}" \ No newline at end of file + = turbo_stream.update dom_id(sc, :position), "#{sc.page.position}.#{sc.element.position}.#{sc.position}" + = turbo_stream.replace dom_id(element, :frame), element \ No newline at end of file diff --git a/app/views/exports/show.html.slim b/app/views/exports/show.html.slim index 2a00726..2f28bea 100644 --- a/app/views/exports/show.html.slim +++ b/app/views/exports/show.html.slim @@ -30,6 +30,8 @@ h2 Testbericht h3 = "#{page.position} #{page.path}" - page.elements { |e| e.success_criteria.any? { _1.failed? } }.each do |element| h4 = "#{element.number} #{element.title}" + = safe_display(element.screenshot) { image_tag(_1.representation(resize_to_fit: [250, 250]))} + = element.description - element.success_criteria.select{ _1.failed? }.each do |sc| h5 = "#{sc.number} #{sc.title}" - if sc.test_comment? diff --git a/bin/jobs b/bin/jobs new file mode 100755 index 0000000..dcf59f3 --- /dev/null +++ b/bin/jobs @@ -0,0 +1,6 @@ +#!/usr/bin/env ruby + +require_relative "../config/environment" +require "solid_queue/cli" + +SolidQueue::Cli.start(ARGV) diff --git a/config/database.yml b/config/database.yml index 56d378d..843b079 100644 --- a/config/database.yml +++ b/config/database.yml @@ -11,8 +11,13 @@ default: &default default_transaction_mode: IMMEDIATE development: - <<: *default - database: storage/development.sqlite3 + primary: + <<: *default + database: storage/development.sqlite3 + queue: + <<: *default + database: storage/development_queue.sqlite3 + migrations_paths: db/queue_migrate # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". @@ -22,5 +27,10 @@ test: database: storage/test.sqlite3 production: - <<: *default - database: storage/production.sqlite3 + primary: + <<: *default + database: storage/production.sqlite3 + queue: + <<: *default + database: storage/production_queue.sqlite3 + migrations_paths: db/queue_migrate diff --git a/config/environments/development.rb b/config/environments/development.rb index c6145f2..a0f127e 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -60,6 +60,8 @@ Rails.application.configure do # Highlight code that enqueued background job in logs. config.active_job.verbose_enqueue_logs = true + config.active_job.queue_adapter = :solid_queue + config.solid_queue.connects_to = { database: { writing: :queue } } # Suppress logger output for asset requests. config.assets.quiet = true @@ -67,7 +69,7 @@ Rails.application.configure do # Raises error for missing translations. # config.i18n.raise_on_missing_translations = true - # Annotate rendered view with file names. + # Annotate rendered view with file nactive_jobames. config.action_view.annotate_rendered_view_with_filenames = true # Uncomment if you wish to allow Action Cable access from any origin. diff --git a/config/environments/production.rb b/config/environments/production.rb index b20131b..399551b 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -73,7 +73,9 @@ Rails.application.configure do # config.cache_store = :mem_cache_store # Use a real queuing backend for Active Job (and separate queues per environment). - # config.active_job.queue_adapter = :resque + config.active_job.queue_adapter = :solid_queue + config.solid_queue.connects_to = { database: { writing: :queue } } + # config.active_job.queue_name_prefix = "a11yist_production" # Disable caching for Action Mailer templates even if Action Controller diff --git a/config/puma.rb b/config/puma.rb index 60e1b9c..5968b47 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -28,6 +28,7 @@ port ENV.fetch("PORT", 3000) # Allow puma to be restarted by `bin/rails restart` command. plugin :tmp_restart +plugin :solid_queue # Only use a pidfile when requested pidfile ENV["PIDFILE"] if ENV["PIDFILE"] diff --git a/config/queue.yml b/config/queue.yml new file mode 100644 index 0000000..9eace59 --- /dev/null +++ b/config/queue.yml @@ -0,0 +1,18 @@ +default: &default + dispatchers: + - polling_interval: 1 + batch_size: 500 + workers: + - queues: "*" + threads: 3 + processes: <%= ENV.fetch("JOB_CONCURRENCY", 1) %> + polling_interval: 0.1 + +development: + <<: *default + +test: + <<: *default + +production: + <<: *default diff --git a/config/recurring.yml b/config/recurring.yml new file mode 100644 index 0000000..8486884 --- /dev/null +++ b/config/recurring.yml @@ -0,0 +1,10 @@ +# production: +# cleanup_session: +# class: CleanSoftDeletedRecordsJob +# queue: background +# args: [ 1000, { batch_size: 500 } ] +# schedule: every hour +# periodic_command: +# command: "SoftDeletedRecord.due.delete_all" +# priority: 2 +# schedule: at 5am every day diff --git a/db/queue_schema.rb b/db/queue_schema.rb new file mode 100644 index 0000000..85194b6 --- /dev/null +++ b/db/queue_schema.rb @@ -0,0 +1,129 @@ +ActiveRecord::Schema[7.1].define(version: 1) do + create_table "solid_queue_blocked_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.string "queue_name", null: false + t.integer "priority", default: 0, null: false + t.string "concurrency_key", null: false + t.datetime "expires_at", null: false + t.datetime "created_at", null: false + t.index [ "concurrency_key", "priority", "job_id" ], name: "index_solid_queue_blocked_executions_for_release" + t.index [ "expires_at", "concurrency_key" ], name: "index_solid_queue_blocked_executions_for_maintenance" + t.index [ "job_id" ], name: "index_solid_queue_blocked_executions_on_job_id", unique: true + end + + create_table "solid_queue_claimed_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.bigint "process_id" + t.datetime "created_at", null: false + t.index [ "job_id" ], name: "index_solid_queue_claimed_executions_on_job_id", unique: true + t.index [ "process_id", "job_id" ], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id" + end + + create_table "solid_queue_failed_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.text "error" + t.datetime "created_at", null: false + t.index [ "job_id" ], name: "index_solid_queue_failed_executions_on_job_id", unique: true + end + + create_table "solid_queue_jobs", force: :cascade do |t| + t.string "queue_name", null: false + t.string "class_name", null: false + t.text "arguments" + t.integer "priority", default: 0, null: false + t.string "active_job_id" + t.datetime "scheduled_at" + t.datetime "finished_at" + t.string "concurrency_key" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index [ "active_job_id" ], name: "index_solid_queue_jobs_on_active_job_id" + t.index [ "class_name" ], name: "index_solid_queue_jobs_on_class_name" + t.index [ "finished_at" ], name: "index_solid_queue_jobs_on_finished_at" + t.index [ "queue_name", "finished_at" ], name: "index_solid_queue_jobs_for_filtering" + t.index [ "scheduled_at", "finished_at" ], name: "index_solid_queue_jobs_for_alerting" + end + + create_table "solid_queue_pauses", force: :cascade do |t| + t.string "queue_name", null: false + t.datetime "created_at", null: false + t.index [ "queue_name" ], name: "index_solid_queue_pauses_on_queue_name", unique: true + end + + create_table "solid_queue_processes", force: :cascade do |t| + t.string "kind", null: false + t.datetime "last_heartbeat_at", null: false + t.bigint "supervisor_id" + t.integer "pid", null: false + t.string "hostname" + t.text "metadata" + t.datetime "created_at", null: false + t.string "name", null: false + t.index [ "last_heartbeat_at" ], name: "index_solid_queue_processes_on_last_heartbeat_at" + t.index [ "name", "supervisor_id" ], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true + t.index [ "supervisor_id" ], name: "index_solid_queue_processes_on_supervisor_id" + end + + create_table "solid_queue_ready_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.string "queue_name", null: false + t.integer "priority", default: 0, null: false + t.datetime "created_at", null: false + t.index [ "job_id" ], name: "index_solid_queue_ready_executions_on_job_id", unique: true + t.index [ "priority", "job_id" ], name: "index_solid_queue_poll_all" + t.index [ "queue_name", "priority", "job_id" ], name: "index_solid_queue_poll_by_queue" + end + + create_table "solid_queue_recurring_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.string "task_key", null: false + t.datetime "run_at", null: false + t.datetime "created_at", null: false + t.index [ "job_id" ], name: "index_solid_queue_recurring_executions_on_job_id", unique: true + t.index [ "task_key", "run_at" ], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true + end + + create_table "solid_queue_recurring_tasks", force: :cascade do |t| + t.string "key", null: false + t.string "schedule", null: false + t.string "command", limit: 2048 + t.string "class_name" + t.text "arguments" + t.string "queue_name" + t.integer "priority", default: 0 + t.boolean "static", default: true, null: false + t.text "description" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index [ "key" ], name: "index_solid_queue_recurring_tasks_on_key", unique: true + t.index [ "static" ], name: "index_solid_queue_recurring_tasks_on_static" + end + + create_table "solid_queue_scheduled_executions", force: :cascade do |t| + t.bigint "job_id", null: false + t.string "queue_name", null: false + t.integer "priority", default: 0, null: false + t.datetime "scheduled_at", null: false + t.datetime "created_at", null: false + t.index [ "job_id" ], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true + t.index [ "scheduled_at", "priority", "job_id" ], name: "index_solid_queue_dispatch_all" + end + + create_table "solid_queue_semaphores", force: :cascade do |t| + t.string "key", null: false + t.integer "value", default: 1, null: false + t.datetime "expires_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index [ "expires_at" ], name: "index_solid_queue_semaphores_on_expires_at" + t.index [ "key", "value" ], name: "index_solid_queue_semaphores_on_key_and_value" + t.index [ "key" ], name: "index_solid_queue_semaphores_on_key", unique: true + end + + add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_ready_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_recurring_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade + add_foreign_key "solid_queue_scheduled_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade +end From 2f1566718e95d60aaca5966cec53f9af7b4780d4 Mon Sep 17 00:00:00 2001 From: david Date: Tue, 12 Nov 2024 22:49:07 +0100 Subject: [PATCH 02/46] Fix nil pointer --- app/views/elements/_element.html.erb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/elements/_element.html.erb b/app/views/elements/_element.html.erb index a465f6c..30421fa 100644 --- a/app/views/elements/_element.html.erb +++ b/app/views/elements/_element.html.erb @@ -24,8 +24,8 @@ <%= element.description %> <% end %> - <%= safe_display(element.screenshot) do %> - <%= image_tag(_1.variant(:thumbnail), class: "img-fluid", alt: "Screenshot des getesteten Elements") %> + <%= safe_display(element.screenshot.variant(:thumbnail)) do %> + <%= image_tag(_1, class: "img-fluid", alt: "Screenshot des getesteten Elements") %> <% end %> From 84fc1d2d936f81dd019fdccf770c640785eff5cd Mon Sep 17 00:00:00 2001 From: david Date: Tue, 12 Nov 2024 22:57:47 +0100 Subject: [PATCH 03/46] Use blank in save display because file attributes are never nil --- app/helpers/application_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index de268bb..36d1dea 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -20,7 +20,7 @@ module ApplicationHelper end def safe_display(value, &block) - return unless value + return if value.blank? yield(value) end From 934deddec41a037d6d8e59b7c4c539903708eda9 Mon Sep 17 00:00:00 2001 From: david Date: Tue, 12 Nov 2024 23:55:00 +0100 Subject: [PATCH 04/46] Add some toasts --- app/helpers/application_helper.rb | 4 ++++ app/javascript/controllers/index.js | 3 +++ app/javascript/controllers/toast_controller.js | 10 ++++++++++ app/views/elements/create.turbo_stream.slim | 5 ++++- app/views/elements/destroy.turbo_stream.slim | 5 ++++- app/views/elements/update.turbo_stream.slim | 5 ++++- app/views/layouts/_toast.html.slim | 8 ++++++++ app/views/layouts/application.html.slim | 7 +++++++ app/views/pages/update.turbo_stream.slim | 4 +++- app/views/success_criteria/update.turbo_stream.slim | 4 +++- 10 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 app/javascript/controllers/toast_controller.js create mode 100644 app/views/layouts/_toast.html.slim diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 36d1dea..c17a465 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -28,4 +28,8 @@ module ApplicationHelper def current_page_displayed(page) @current_page&.id == page.id ? "open" : nil end + + def turbo_stream_toast(content, alert) + turbo_stream.append("toasts", partial: "layouts/toast", locals: { content:, alert: }) + end end diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index 6803226..1946f61 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -43,5 +43,8 @@ application.register("sortable", SortableController) import ThemeSwitcherController from "./theme_switcher_controller" application.register("theme-switcher", ThemeSwitcherController) +import ToastController from "./toast_controller" +application.register("toast", ToastController) + import UnsavedChangesController from "./unsaved_changes_controller" application.register("unsaved-changes", UnsavedChangesController) diff --git a/app/javascript/controllers/toast_controller.js b/app/javascript/controllers/toast_controller.js new file mode 100644 index 0000000..55d8ea1 --- /dev/null +++ b/app/javascript/controllers/toast_controller.js @@ -0,0 +1,10 @@ +import { Controller } from "@hotwired/stimulus" +import * as bootstrap from "bootstrap" + +// Connects to data-controller="toast" +export default class extends Controller { + connect() { + const toastBootstrap = bootstrap.Toast.getOrCreateInstance(this.element) + toastBootstrap.show() + } +} diff --git a/app/views/elements/create.turbo_stream.slim b/app/views/elements/create.turbo_stream.slim index 6f8cc39..5c75d05 100644 --- a/app/views/elements/create.turbo_stream.slim +++ b/app/views/elements/create.turbo_stream.slim @@ -1,3 +1,6 @@ = turbo_stream.update "new_element_frame", partial: "pages/new_element_button", locals: { page: @element.page } = turbo_stream.append "element_list", @element -= turbo_stream.append dom_id(@element.page, :page_nav_elements), partial: "elements/page_nav_row", locals: { element: @element, current_page: true } \ No newline at end of file += turbo_stream.append dom_id(@element.page, :page_nav_elements), partial: "elements/page_nav_row", locals: { element: @element, current_page: true } + + += turbo_stream_toast("Element hinzugefügt", false) \ No newline at end of file diff --git a/app/views/elements/destroy.turbo_stream.slim b/app/views/elements/destroy.turbo_stream.slim index 6370d37..35f04de 100644 --- a/app/views/elements/destroy.turbo_stream.slim +++ b/app/views/elements/destroy.turbo_stream.slim @@ -2,4 +2,7 @@ = turbo_stream.remove dom_id(@element, :page_nav_row) - @element.page.elements.reject { _1 == @element }.each do |e| = turbo_stream.update dom_id(e, :title), "#{e.page.position}.#{e.position} #{e.title}" - = turbo_stream.replace dom_id(e, :page_nav_row), partial: "elements/page_nav_row", locals: { element: e, current_page: true } \ No newline at end of file + = turbo_stream.replace dom_id(e, :page_nav_row), partial: "elements/page_nav_row", locals: { element: e, current_page: true } + + += turbo_stream_toast("Element wurde gelöscht", false) \ No newline at end of file diff --git a/app/views/elements/update.turbo_stream.slim b/app/views/elements/update.turbo_stream.slim index 64c6489..a6f1e78 100644 --- a/app/views/elements/update.turbo_stream.slim +++ b/app/views/elements/update.turbo_stream.slim @@ -5,4 +5,7 @@ / = turbo_stream.update dom_id(sc, :title), "#{sc.page.position}.#{sc.element.position}.#{sc.position} #{sc.title}" - element.success_criteria.each do |sc| = turbo_stream.update dom_id(sc, :position), "#{sc.page.position}.#{sc.element.position}.#{sc.position}" - = turbo_stream.replace dom_id(element, :frame), element \ No newline at end of file + = turbo_stream.replace dom_id(element, :frame), element + + += turbo_stream_toast("Element gespeichert", false) \ No newline at end of file diff --git a/app/views/layouts/_toast.html.slim b/app/views/layouts/_toast.html.slim new file mode 100644 index 0000000..920e3fb --- /dev/null +++ b/app/views/layouts/_toast.html.slim @@ -0,0 +1,8 @@ +.toast class="#{alert ? "text-bg-danger" : ""}" role="alert" aria-live="assertive" aria-atomic="true" data={ controller: "toast" } + .toast-header + /img src="..." class="rounded me-2" alt="..."> + /strong.me-auto = heading + .small.me-auto = l(Time.current.to_time) + button.btn-close type="button" data-bs-dismiss="toast" aria-label="Schliessen" + .toast-body + = content diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim index 18a65aa..992a528 100644 --- a/app/views/layouts/application.html.slim +++ b/app/views/layouts/application.html.slim @@ -16,5 +16,12 @@ html data-bs-theme="#{cookies[:"modeTheme"] || "light"}" data-controller="set-th main.col.ps-md-2.pt-2 #main-content[data-controller="rich-text-link-targets"] = yield + + .toast-container.position-fixed.bottom-0.start-0.p-3 id="toasts" + - if flash.alert + = render partial: "layouts/toast", locals: { content: flash.alert, alert: true } + - if flash.notice + = render partial: "layouts/toast", locals: { content: flash.notice, alert: false } + /footer.container-fluid.mt-auto.border-top = Rails.configuration.build_version && "Version: #{Rails.configuration.build_version}" diff --git a/app/views/pages/update.turbo_stream.slim b/app/views/pages/update.turbo_stream.slim index 298fcff..6bad9ea 100644 --- a/app/views/pages/update.turbo_stream.slim +++ b/app/views/pages/update.turbo_stream.slim @@ -4,4 +4,6 @@ = turbo_stream.update dom_id(element, :title), "#{element.page.position}.#{element.position} #{element.title}" = turbo_stream.replace dom_id(element, :page_nav_row), partial: "elements/page_nav_row", locals: { element: element, current_page: element.page == @page} - element.success_criteria.each do |sc| - = turbo_stream.update dom_id(sc, :position), "#{sc.page.position}.#{sc.element.position}.#{sc.position}" \ No newline at end of file + = turbo_stream.update dom_id(sc, :position), "#{sc.page.position}.#{sc.element.position}.#{sc.position}" + += turbo_stream_toast("Pfad gespeichert", false) \ No newline at end of file diff --git a/app/views/success_criteria/update.turbo_stream.slim b/app/views/success_criteria/update.turbo_stream.slim index 72fe6ae..dc9c79e 100644 --- a/app/views/success_criteria/update.turbo_stream.slim +++ b/app/views/success_criteria/update.turbo_stream.slim @@ -2,4 +2,6 @@ = turbo_stream.replace dom_id(@success_criterion, :body), partial: "success_criteria/body", locals: {success_criterion: @success_criterion } - @success_criterion.element.success_criteria.each do |sc| - = turbo_stream.update(dom_id(sc, :position), sc.number) \ No newline at end of file + = turbo_stream.update(dom_id(sc, :position), sc.number) + += turbo_stream_toast("Erfolgskriterium gespeichert: #{t("activerecord.attributes.success_criterion.results/#{@success_criterion.result}")}", false) \ No newline at end of file From fab49815081de90e4e6d85921238926c82fbc49a Mon Sep 17 00:00:00 2001 From: david Date: Wed, 13 Nov 2024 00:26:07 +0100 Subject: [PATCH 05/46] Add lightbox --- .../stylesheets/application.bootstrap.scss | 12 ++-- app/javascript/controllers/index.js | 3 + app/views/elements/_element.html.erb | 12 ++-- package.json | 3 +- vendor/assets/fonts/lg.svg | 54 ++++++++++++++++++ vendor/assets/fonts/lg.ttf | Bin 0 -> 4756 bytes vendor/assets/fonts/lg.woff | Bin 0 -> 4832 bytes vendor/assets/fonts/lg.woff2 | Bin 0 -> 2332 bytes vendor/assets/images/loading.gif | Bin 0 -> 4178 bytes yarn.lock | 12 ++++ 10 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 vendor/assets/fonts/lg.svg create mode 100644 vendor/assets/fonts/lg.ttf create mode 100644 vendor/assets/fonts/lg.woff create mode 100644 vendor/assets/fonts/lg.woff2 create mode 100644 vendor/assets/images/loading.gif diff --git a/app/assets/stylesheets/application.bootstrap.scss b/app/assets/stylesheets/application.bootstrap.scss index 19d4485..65faf19 100644 --- a/app/assets/stylesheets/application.bootstrap.scss +++ b/app/assets/stylesheets/application.bootstrap.scss @@ -28,13 +28,8 @@ $enable-rounded: false; @import 'bootstrap/scss/bootstrap'; -@font-face { - font-display: block; - font-family: "bootstrap-icons"; - src: url("./bootstrap-icons.woff2") format("woff2"), - url("./bootstrap-icons.woff") format("woff"); -} - +$lg-path-images: ""; +$bootstrap-icons-font-dir: ""; @import 'bootstrap-icons/font/bootstrap-icons'; @@ -52,6 +47,9 @@ $enable-rounded: false; @import "trix/dist/trix"; +$lg-path-fonts: ""; + +@import "lightgallery/scss/lightgallery"; /* * Provides a drop-in pointer for the default Trix stylesheet that will format the toolbar and * the trix-editor content (whether displayed or under editing). Feel free to incorporate this diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index 1946f61..8654ab4 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -48,3 +48,6 @@ application.register("toast", ToastController) import UnsavedChangesController from "./unsaved_changes_controller" application.register("unsaved-changes", UnsavedChangesController) + +import Lightbox from '@stimulus-components/lightbox' +application.register('lightbox', Lightbox) \ No newline at end of file diff --git a/app/views/elements/_element.html.erb b/app/views/elements/_element.html.erb index 30421fa..1c939d8 100644 --- a/app/views/elements/_element.html.erb +++ b/app/views/elements/_element.html.erb @@ -24,14 +24,14 @@ <%= element.description %> <% end %> - <%= safe_display(element.screenshot.variant(:thumbnail)) do %> - <%= image_tag(_1, class: "img-fluid", alt: "Screenshot des getesteten Elements") %> + <% safe_display(element.screenshot) do |s| %> +
+ <%= link_to(s) do %> + <%= image_tag(s.variant(:thumbnail), class: "img-fluid", alt: "Screenshot des getesteten Elements") %> + <% end %> +
<% end %> - -

- -

<% end %>
diff --git a/package.json b/package.json index 148b0d9..b8c7a06 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "@popperjs/core": "^2.11.8", "@rails/actiontext": "^7.1.3-4", "@rails/request.js": "^0.0.11", + "@stimulus-components/lightbox": "^4.0.0", "autoprefixer": "^10.4.19", "bootstrap": "^5.3.3", "bootstrap-icons": "^1.11.3", @@ -32,4 +33,4 @@ "devDependencies": { "tabby-agent": "^1.7.0" } -} \ No newline at end of file +} diff --git a/vendor/assets/fonts/lg.svg b/vendor/assets/fonts/lg.svg new file mode 100644 index 0000000..fe8b075 --- /dev/null +++ b/vendor/assets/fonts/lg.svg @@ -0,0 +1,54 @@ + + + + + + +{ + "fontFamily": "lg", + "majorVersion": 2, + "minorVersion": 0, + "fontURL": "", + "copyright": "", + "license": "", + "licenseURL": "", + "description": "Font generated by IcoMoon.", + "version": "Version 2.0", + "fontId": "lg", + "psName": "lg", + "subFamily": "Regular", + "fullName": "lg" +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/vendor/assets/fonts/lg.ttf b/vendor/assets/fonts/lg.ttf new file mode 100644 index 0000000000000000000000000000000000000000..825f4832d409eaebc9684ea59d1e599c364f721d GIT binary patch literal 4756 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 T$r4sSXOiGKDXfAxF literal 0 HcmV?d00001 diff --git a/vendor/assets/fonts/lg.woff2 b/vendor/assets/fonts/lg.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2c2e289260bf80880c447749205a7024c34310f0 GIT binary patch literal 2332 zcmV+%3FG#6Pew8T0RR9100|rb3jhEB01}7*00_+h0RR9100000000000000000000 z00006U;u$65eN#LES5?OIsgGS0we=)1Rw>1N(UetsSW_<=W(-oKMAExFsmpPS1K05 zm`JkguSY3=L2BQ9_!?p4ex41!yLOc&r zDT^p(Lo)AyuQlo9HLa2 zByzBWk}@|mNkOuaW+ht7^5wbI?CAO*9J5*jr_QF)i!&HbG#?g{+ddf%B->T{L(HqH&)%|{kM!vaiGD2Wk9^HhF(ny5FXcdS-1k6z*iIF(Do?5A8 zN6jh51JpV{W!W1?#U56Yi%GK;fTGvY`Ex;vf~>k_m+$Yo&4@GKrES#US!7EAXdg}@WV@oiTL(^Q zz5b>N*1ehcK<&7u9k{NNbk=uu6mc911o~bF7|x8yhrZIszRYnSu>LIC%Br1LQcO?$ zlqFa(6>PW_L?r9e-S)L!z8Bgc*?v_;n;e6ho3*dT{UXJ4T|j-qU*+}3AMIeQ&7w5{ zJ5|2S20Gm6E^0Iu zxr&U9MVbTMbkiof$ahDt@J!1SeL`WH+c?jiwrkh2lgr#5D+-rl+M{sGr$$Hw_Cx+f z=kJFf%d{XGW{_SCvJZ+Of}@U+)$+Sk)vEc+yE0H^}yY^rPb zKw5gBJ+@ojxnubM7PPh&TqMIo3#IC;Te8~`h3$r-LPL8|_(W{Rjgah69qi%lg+&I# z{Y}a;ZV9u6p)s1ATmQceY5zSgWmdKoZ@nd<-nLX)*-RBHRuItN=IVrZV&XsZKYsOu zSxN1Vbn>2(Hfky7E!fXci_6;Kg(mtGLoF?7o6mUi>c=f5E2)&D%&F)fmbKinOn9$$ zxC>^uTf76MSjNy0n@MM~Ze9dS3+Qlr`5X5|uUmyU{PBiGNsE);c_&$DShQh71wtxI z-r$px@q$1}kB{)A4SW0Agkx0kGEsZ?f@z_b z#)f@S;pjC0A~5o=|H?>LU z-+M#fHjwR0KQyP;yg!XJM&1mc+--K4aJjcQjq#Vs=DUKfBsFdcFC_^J^IEcAABFm?~JPvLWH6GZ7lJ_ zx^5gWP>(NDJzU#Rbm7Y!t~FKGuQSXgNs?>-Pt3O0_RF&}X~pWPwacX?nq_%Rimdhj zUvwD+zpIz%Q(EMI*b93u78PI6L9s&GsxM1gAG*ZA%Uh*-<=nP{!yB_VrbFJBFgxo> z^i1nV9;1|hpkUVrLaW4^696D#ZC*S}fSu%rvH@3A<%mAobH*tzmjelomOAjHhfJB$ zPkRzQo!Q7;LKT7Qa`SdGMb*RTPMEx1AXH*eG8#_F1-iszG-bNG&kq2g2NnT_yKy6| z0TF%{ZvzAX7=Se@)3#Dt`L|Gt004j=b?yB~9>>q(zr`PK0z(0VMg;KkgbEo4~mUH~pO5l?vLTezY|&tu%iGVZ&~wX_mmvWmQm&hN7E= z!09nKV6dMN+#VwU3TO)P@uPJlMA$IX0})}EWz=IZkiZ9o`}jd3zz1Rkpn;VX0)BiW z5(*)1+eX+18{Z_p?$wA5ZEJV_rZMM0Gwg&NurFE%v|a;VzXw*^K&Ex}`ZaJ^s&9j} zk<`gWyzw8}ZZJPP7nr1my|CGh7|Mri$Q{@q89y~{IrIgCk@oMJR>L0mnF9_D0000L C#$m<) literal 0 HcmV?d00001 diff --git a/vendor/assets/images/loading.gif b/vendor/assets/images/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..4744c4556c71275769cdd16f1194c2f77cb6153c GIT binary patch literal 4178 zcmd7VX;c&Gng{SoZAq$Xt**i^&?!oH}0 zVv8`e0RaK80&d`?Z36*iUj-FyMFqqi)E@2WcD(VbLQN8&V4`Ec~70+`9IIU zO6KYA>=Mocc)%|J;Nal!`SWKdC#TJuH;Y7~)vH$r1qGFql&DmyRjXD>BoaqQ$DEv; zqM{<1Ocog#xqtuuwQJYL#l^|x^4{LwmX?;r#>SH;Pww8md(E0P?d|O~H8t1mBdqf{z^zw6Ea;Zfm9(aLazUF6Q~N`U_>OviLj*?xJAF9-noS(qB@VP)jZk;Qde`RI4J zomp;!mCEUz`sG2{2ts=A_p>ReD0U@nNF=^VG<@jxW?=~bUrYxWq9oDIEeE6vB48jL zP_pAuR0>C!9r_laD}yEUtpH4=GQ7k@gaHCU2(FBaVJqsLxyTz<6b22CuT{E7FAFK;waGV5c?oEp13xTW znXAXYYw>FVeMPA7&7B@iENw{XX~v4d)Z;$<^_d|trG~=WXBdkdce(EJa6BB^>wnzx zSUB;=l$hz^FO=3slADhvZA?q$of-ojB@=@-S~1WJT^gPIY?$%$30dIy=G4qe&#qh~ z!a$ctJtdJMw;yO*Y!72+^%3K2cRw1?KRWJ@6|3U>#l_>Jel$v|fb9K>K{R;lM7CNoB-> z$s&B4|1KydK%%cNF2>kLgE1UOkeV1m0vVE|f!JglR-1TK1k*Mdm^F}QTQ42VqBE}> zh6i1W3S7`&N~8qVY~N=*jG7d08XV&EI-T8?nFu!F-|ne(a_VvXTRRDPjAlokW
X}UWz08V_@o{j+otpay;*D-dGqNeK?82 zSFiTSQS#Yy(sZh>b1?x_Iy(PWMH{Xk&4b<6p#R2H>n8GFv%E#oV`YvN?= zi_Obrz;S0auSuLkk*;)Eea!7-D#HgoG~1%;`pTlr$kwA!SYv5Hb@s^k`eOGwCzk~a z44pOVeyM09!0A4!9JF%*RvIcs?0x)!z;|x15(61ONWg9>jWbY25)o6#90_S zBkzEJL8RDZAq4FO;<9}JBqP(rhmMv(Xt-=w0?w;n2+Q-Z6JZz1SQXy*rYdhb#A}!T z6jm<9(+ASK13}iUG1jK@cW-*Ls1F`enRoB@hW$MGfZ0Ddlw3JJcD3*I<=4UYBq&Kt zdx6n~Ga+iBn-)^siIr{;g+XqZQ3(60A^lh*h0mcm_&wr77U#cT_l<5Df>f?+4i9vL z+H$SSIIb6^k{*fy-@`Bc+~D^sK#prh?9&+!^R)o0Dnm#Dn#%l)L$ew;bK@$f)1YzT z+tcPYU#s;ihbknaK_3TLkDl1k{!x-r`d;9_C<`NZMtg|=+Y#TCty4@sQ&cI=!%D)= z9g)}MM`aQ>a(H^>oacx1S*Z@_zKbOhJ7cLsvpk8eL5ybc84 zmawSM^R1<-C>oW@M4Qk3n1B$!P-z@{|3$f@OCci~ElxA}5VEL`UcY6JhC;zeQ^I6O zZC>Pl&5mL`RG|d`rv2lD<&){98$3NqC1+G=nF+gtx>wTy@W#^Yr%uefE>$4HyrTKY z76s%`4(T6s>vp_k<;yp-3GM8@CAfWp<4n6{@DEGk|KBf7+fm!Ebh4 z9~)86xSCIKdQPWWo!iT(AM3`W%;sLBk^3>M=j(KZR z4$o>R27`PC%ZWGxgJPQa2o@)t*EZ;LMMRMRZl@3{^JAI4Rw|VF<~>aL=ZoPVFtYAk zN2naA?`#;M>vhY7@?NQKLvl$pp+#m=Y4+A~k%(^e+;GXHDi2sU4OBge4Kso;(wa23 z=aEuEvV|SqSaFX4iB&*s?4)oNK}LMKcv%5SKuho48->H#2L7ClkPwYn`8wp;D}&T( zzOQcq@F&0fH>M~Z)cXq7NoQeO1BJ9DT)a9oskWcVLi9T-Qx?|Ioll@~ttfy;FNL8Sm}6CV{U%4WtvMXoBArxt^}(4HL?*dKs8~ zAXmyu(qDL>>lr|hu|2A4SzL8v$2W_nen4qQ%sI|NVJbug?bx|e37DQJ#-fXZ!a_-;JeW;taBCZunjBFR`G}lFa*K7F(I){-27070Ix>pF{Sn~Z3BZ(m@Q~E&SIDL@U9UV&o|?gi zZ;gy6;}icj7~20a5QLc3QEGVWmee*C#F*(|pUwa>(y6>YXtX{Q-K58J1o#S7_BT6& z4nGTs=xWn_z{_<3ZA){<9aRBUsa@Gr-fzsXnVZMYJGw_P8jBxOHD|vAutqo}pvZ2; z!VuRbn0j;tXuH_IbJSJ~=q38?+M9e>;BUXb6!~9=*@?_aJFRx!CNL={0=#T(^v5l# zMpcZ{c>>@Q!$8LMF*`TF`MHy_*RtwEL0FQ6RIt7a1!U}C0dUd?Gi zM$X1StY`6#kv}nv=O_i`-co(z5@!t{3BBQHb+2PyoW-{|P8+I@GIuz_y7aZ^0&g^J z1J>6cAe6LatWY?g`uJ>xo; literal 0 HcmV?d00001 diff --git a/yarn.lock b/yarn.lock index bf77f5c..b1abead 100644 --- a/yarn.lock +++ b/yarn.lock @@ -200,6 +200,13 @@ resolved "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz" integrity sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg== +"@stimulus-components/lightbox@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@stimulus-components/lightbox/-/lightbox-4.0.0.tgz#dbe56146a3d14582a13e568406292d52aa56cbb0" + integrity sha512-pj4PfnGANbc3Ef6I/5aPeRS8Tj2rK1b9dn8rlpJZs1K9narqM57ZtUTutHZsnOv95+4LqQVktj+auMgz+LKzjw== + dependencies: + lightgallery "^2.7.2" + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" @@ -516,6 +523,11 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" +lightgallery@^2.7.2: + version "2.7.2" + resolved "https://registry.yarnpkg.com/lightgallery/-/lightgallery-2.7.2.tgz#9309216986fbce60602b851e1ef80cf40769c3d3" + integrity sha512-Ewdcg9UPDqV0HGZeD7wNE4uYejwH2u0fMo5VAr6GHzlPYlhItJvjhLTR0cL0V1HjhMsH39PAom9iv69ewitLWw== + lilconfig@^3.1.1: version "3.1.2" resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz" From 3e2cef9907ffecc641d3de33bf624d1df304d2aa Mon Sep 17 00:00:00 2001 From: david Date: Sun, 17 Nov 2024 00:45:23 +0100 Subject: [PATCH 06/46] GUI cosmetics --- app/views/elements/_element.html.erb | 2 +- app/views/elements/_page_nav_row.html.slim | 20 +++++++------------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/app/views/elements/_element.html.erb b/app/views/elements/_element.html.erb index 1c939d8..1571085 100644 --- a/app/views/elements/_element.html.erb +++ b/app/views/elements/_element.html.erb @@ -25,7 +25,7 @@
<% end %> <% safe_display(element.screenshot) do |s| %> -
+
<%= link_to(s) do %> <%= image_tag(s.variant(:thumbnail), class: "img-fluid", alt: "Screenshot des getesteten Elements") %> <% end %> diff --git a/app/views/elements/_page_nav_row.html.slim b/app/views/elements/_page_nav_row.html.slim index 29713b4..7ab20f2 100644 --- a/app/views/elements/_page_nav_row.html.slim +++ b/app/views/elements/_page_nav_row.html.slim @@ -1,13 +1,7 @@ -li id=dom_id(element, :page_nav_row) data={ "sortable-url": element_path(element), "form-name": "element", "position-attribute": "position" } - - if current_page - =< link_to("##{dom_id(element)}", data: { "turbo": false }) do - i.bi.bi-boxes.me-2 - span id=dom_id(element, :page_nav_title) - = "#{element.number} #{element.title}" - i.bi.bi-grip-vertical.float-end.handle - - else - =< link_to(report_path(element.report, page_id: element.page.id, anchor: dom_id(element)), data: { "turbo": false }) do - i.bi.bi-boxes.me-2 - span id=dom_id(element, :page_nav_title) - =< "#{element.number} #{element.title}" - i.bi.bi-grip-vertical.float-end.handle \ No newline at end of file +li.d-flex id=dom_id(element, :page_nav_row) data={ "sortable-url": element_path(element), "form-name": "element", "position-attribute": "position" } + - url = current_page ? "##{dom_id(element)}" : report_path(element.report, page_id: element.page.id, anchor: dom_id(element)) + i.bi.bi-boxes.me-2 + =< link_to(url, data: { "turbo": false }, class: "me-auto") do + span id=dom_id(element, :page_nav_title) + = "#{element.number} #{element.title}" + i.bi.bi-grip-vertical.handle From c2587540697335c2f8c55c80974188c8dfb1c2ef Mon Sep 17 00:00:00 2001 From: david Date: Sun, 17 Nov 2024 00:45:41 +0100 Subject: [PATCH 07/46] Ask before deleting report --- app/views/reports/show.html.slim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/reports/show.html.slim b/app/views/reports/show.html.slim index 13f31c0..405b87c 100644 --- a/app/views/reports/show.html.slim +++ b/app/views/reports/show.html.slim @@ -61,4 +61,4 @@ h1 .action-row = link_to t("scaffold.link_edit", model: @report.model_name.human), edit_report_path(@report) = link_to t("scaffold.link_index", model: @report.model_name.human(count: 2)), reports_path - = button_to t("scaffold.link_destroy", model: @report.model_name.human), @report, method: :delete, class: "btn btn-outline-danger" + = button_to t("scaffold.link_destroy", model: @report.model_name.human), @report, method: :delete, class: "btn btn-outline-danger", data: { turbo_confirm: "Bist du sicher?"} From 65568ac27b4ec03128c80f0ac99a5329140aea30 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 17 Nov 2024 00:46:01 +0100 Subject: [PATCH 08/46] Improve editor/dockerfile --- Dockerfile | 55 +++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/Dockerfile b/Dockerfile index d7dcac6..6ae5155 100644 --- a/Dockerfile +++ b/Dockerfile @@ -56,20 +56,53 @@ RUN \ bundle config set app_config ${GEM_HOME} ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 +ARG HELIX_VERSION=24.07 +RUN curl -L https://github.com/helix-editor/helix/releases/download/${HELIX_VERSION}/helix-${HELIX_VERSION}-x86_64-linux.tar.xz -o /tmp/helix.tar.xz && \ + cd /opt/ && \ + tar -xf /tmp/helix.tar.xz && \ + ln -s /opt/helix-${HELIX_VERSION}-x86_64-linux/hx /usr/local/bin chmod +x /usr/local/bin/ USER ${NAME} -RUN mkdir -p ~/.config/fish && echo "set fish_greeting" >> ~/.config/fish/config.fish -RUN \ - echo >> ~/.config/fish/config.fish &&\ - echo "function fish_prompt" >> ~/.config/fish/config.fish &&\ - echo " set -l last_status \$status" >> ~/.config/fish/config.fish &&\ - echo " set -l stat" >> ~/.config/fish/config.fish &&\ - echo " if test \$last_status -ne 0" >> ~/.config/fish/config.fish &&\ - echo " set stat (set_color red)\"[\$last_status]\"(set_color normal)" >> ~/.config/fish/config.fish &&\ - echo " end" >> ~/.config/fish/config.fish &&\ - echo " string join '' -- (set_color green) (prompt_pwd) (set_color normal) \$stat ' 🐳 '" >> ~/.config/fish/config.fish && \ - echo "end" >> ~/.config/fish/config.fish +RUN mkdir -p ~/.config/fish ~/.config/helix + +COPY < Date: Sun, 17 Nov 2024 00:46:22 +0100 Subject: [PATCH 09/46] Basic report export --- app/views/exports/show.html.slim | 150 +++++++++++++++++++++++++------ 1 file changed, 125 insertions(+), 25 deletions(-) diff --git a/app/views/exports/show.html.slim b/app/views/exports/show.html.slim index 2f28bea..a4bfc07 100644 --- a/app/views/exports/show.html.slim +++ b/app/views/exports/show.html.slim @@ -1,6 +1,88 @@ +/ 1.0 Einschtzung +/ 1.1 Zielsetzung und Ausgangslage +/ 1.2 Wie wurde getestet +/ 1.3 Einschätzung + +/ 2.0 Protokoll +/ 2.1. Seite +/ -Pfad +/ 2.1.1 Element +/ -Position (nur wenn Inhalt) +/ -Kommentar (nur wenn Inhalt) +/ 2.1.1.1 Check +/ -Quick Criteria +/ -Quick Fail +/ -Quick Fix +/ -Kommentar (nur wenn Inhalt) +/ 2.2. Seite +/ 2.2.1 Element +/ 2.2.1.1 Check + +/ 3.0 Anahng +/ 3.1.Check +/ -WCAG Nr. (nur wenn Inhalt) +/ -WCAG Link (nur wenn Inhalt) +/ -Konformität (nur wenn Inhalt) +/ -Anmerkung Konformität (nur wenn Inhalt) +/ -Priorität (nur wenn Inhalt) +/ -Kriterium/Grundlage (nur wenn Inhalt) +/ -Ausnahmen (nur wenn Inhalt) +/ -Verstehen (nur wenn Inhalt) +/ -Beispiel (nur wenn Inhalt)- +/ -Anmerkung (nur wenn Inhalt) +/ -Links (nur wenn Inhalt) + +css: + .trix-content:has(*) { + font-family: "Serif"; + border: solid white 1px; + padding: 1rem; + } + dd { + margin-left: 10rem; + } + h1 id=dom_id(@report) = @report.name -h2#toc Inhaltsverzeichnis +h2 1 Einschätzung +p Gibt es hier Text? + +h3 1.1 Zielsetzung und Ausgangslage +p Woher kommt dieser Text? + +h3 1.2 Wie wurde getestet +p Woher kommt dieser Text? + +h3 1.3 Einschätzung +p Woher kommt dieser Text? + +h2 2 Protokoll +- current_page_pos = 0 +- @report.pages.select { |p| p.elements.any? { |e| e.success_criteria.any? { _1.failed? } } }.each do |page| + - current_page_pos += 1 + - current_element_pos = 0 + h3 = "2.#{current_page_pos} #{page.path}" + - page.elements { |e| e.success_criteria.any? { _1.failed? } }.each do |element| + - current_element_pos += 1 + - current_sc_pos = 0 + h4 = "2.#{current_page_pos}.#{current_element_pos} #{element.title}" + = safe_display(element.screenshot) { image_tag(_1.representation(resize_to_fit: [250, 250]))} + = element.description + - element.success_criteria.select{ _1.failed? }.each do |sc| + - current_sc_pos += 1 + h5 = "2.#{current_page_pos}.#{current_element_pos}.#{current_sc_pos} #{sc.title}" + - if sc.test_comment? + p = sc.test_comment + dl + dt Kriterium + dd = sc.quick_criterion + dt Fail + dd = sc.quick_fail + dt Fix + dd = sc.quick_fix + dt WCAG + dd = link_to(sc.check.external_number, sc.check.external_url) + nav = link_to(@report.name, "##{dom_id(@report)}") ul @@ -25,33 +107,51 @@ nav li = link_to(check.display_label) -h2 Testbericht -- @report.pages.select { |p| p.elements.any? { |e| e.success_criteria.any? { _1.failed? } } }.each do |page| - h3 = "#{page.position} #{page.path}" - - page.elements { |e| e.success_criteria.any? { _1.failed? } }.each do |element| - h4 = "#{element.number} #{element.title}" - = safe_display(element.screenshot) { image_tag(_1.representation(resize_to_fit: [250, 250]))} - = element.description - - element.success_criteria.select{ _1.failed? }.each do |sc| - h5 = "#{sc.number} #{sc.title}" - - if sc.test_comment? - p = sc.test_comment - dl - dt Kriterium - dd = sc.quick_criterion - dt Fail - dd = sc.quick_fail - dt Fix - dd = sc.quick_fix - dt WCAG - dd = link_to(sc.check.external_number, sc.check.external_url) h2 Anhang h3 Liste der zu beachtenden WCAG Regeln -- @failed_success_criteria.group_by(&:check).each do |check, scs| +- @failed_success_criteria.group_by(&:check).sort_by{ |c, scs| c.number }.each do |check, scs| h4 = check.display_label - = check.criterion_de - strong Erfolgskriterien - p = scs.map(&:number).join(", ") + dl + - safe_display(check.external_number) do + dt WCAG Nummer + dd = _1 + - safe_display(check.external_url) do + dt WCAG Link + dd = _1 + - safe_display(check.conformity_level) do + dt Konformität + dd = _1 + - safe_display(check.conformity_notice_de) do + dt Anmerkung Konformität + dd = _1 + - safe_display(check.priority) do + dt Priorität + dd = _1 + - safe_display(check.criterion_de) do + dt Kriterium/Grundlage + dd = _1 + - safe_display(check.exemption_details_de) do + dt Ausnahmen + dd = _1 + - safe_display(check.criterion_details_de) do + dt Verstehen + dd = _1 + - safe_display(check.example_de) do + dt Beispiel + dd = _1 + - safe_display(check.annotation_de) do + dt Anmerkung + dd = _1 + - if check.links.any? + dt Links + dd + - check.links.group_by(&:category).each do |category, links| + strong = category + ul + - links.each do |l| + li = link_to(l.text, l.url) + dt Erfolgskriterien + dd = scs.map(&:number).join(", ") From aa1552db819b0e066d1e9ea9ccce0adc07c239a8 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 17 Nov 2024 00:50:39 +0100 Subject: [PATCH 10/46] Fix test --- test/system/reports_test.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/system/reports_test.rb b/test/system/reports_test.rb index 6703232..066b7f7 100644 --- a/test/system/reports_test.rb +++ b/test/system/reports_test.rb @@ -37,7 +37,9 @@ class ReportsTest < ApplicationSystemTestCase test "should destroy Report" do visit report_url(@report) - click_on "Prüfbericht löschen", match: :first + accept_confirm do + click_on "Prüfbericht löschen", match: :first + end assert_text("Prüfberichte") assert(Report.exists?(@report.id) == false) end From 13755bacdb7c1edae5a7935cc4903f3ec88c6175 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 17 Nov 2024 01:58:47 +0100 Subject: [PATCH 11/46] Improve export html for libreoffice writer --- app/views/exports/show.html.slim | 187 +++++++++++----------------- app/views/layouts/exports.html.slim | 2 +- 2 files changed, 77 insertions(+), 112 deletions(-) diff --git a/app/views/exports/show.html.slim b/app/views/exports/show.html.slim index a4bfc07..4408d2b 100644 --- a/app/views/exports/show.html.slim +++ b/app/views/exports/show.html.slim @@ -1,37 +1,3 @@ -/ 1.0 Einschtzung -/ 1.1 Zielsetzung und Ausgangslage -/ 1.2 Wie wurde getestet -/ 1.3 Einschätzung - -/ 2.0 Protokoll -/ 2.1. Seite -/ -Pfad -/ 2.1.1 Element -/ -Position (nur wenn Inhalt) -/ -Kommentar (nur wenn Inhalt) -/ 2.1.1.1 Check -/ -Quick Criteria -/ -Quick Fail -/ -Quick Fix -/ -Kommentar (nur wenn Inhalt) -/ 2.2. Seite -/ 2.2.1 Element -/ 2.2.1.1 Check - -/ 3.0 Anahng -/ 3.1.Check -/ -WCAG Nr. (nur wenn Inhalt) -/ -WCAG Link (nur wenn Inhalt) -/ -Konformität (nur wenn Inhalt) -/ -Anmerkung Konformität (nur wenn Inhalt) -/ -Priorität (nur wenn Inhalt) -/ -Kriterium/Grundlage (nur wenn Inhalt) -/ -Ausnahmen (nur wenn Inhalt) -/ -Verstehen (nur wenn Inhalt) -/ -Beispiel (nur wenn Inhalt)- -/ -Anmerkung (nur wenn Inhalt) -/ -Links (nur wenn Inhalt) - css: .trix-content:has(*) { font-family: "Serif"; @@ -42,6 +8,29 @@ css: margin-left: 10rem; } +/nav + = link_to(@report.name, "##{dom_id(@report)}") + ul + li = link_to("Inhaltsverzeichnis", "#toc") + li + = link_to('Testbericht') + ul + - @report.pages.select { |p| p.elements.any? { |e| e.success_criteria.any?(&:failed?) } }.each do |page| + li + = link_to("#{page.position} #{page.path}", "##{dom_id(page)}") + ul + - page.elements.select { |e| e.success_criteria.any?(&:failed?) }.each do |element| + li + = link_to("#{element.number} #{element.title}") + ul + - element.success_criteria.select(&:failed?).each do |sc| + li = link_to("#{sc.number} #{sc.title}", "##{dom_id(sc)}") + li + = link_to("Anhang") + ul + - @failed_success_criteria.group_by(&:check).each do |check, scs| + li = link_to(check.display_label) + h1 id=dom_id(@report) = @report.name h2 1 Einschätzung @@ -73,85 +62,61 @@ h2 2 Protokoll h5 = "2.#{current_page_pos}.#{current_element_pos}.#{current_sc_pos} #{sc.title}" - if sc.test_comment? p = sc.test_comment - dl - dt Kriterium - dd = sc.quick_criterion - dt Fail - dd = sc.quick_fail - dt Fix - dd = sc.quick_fix - dt WCAG - dd = link_to(sc.check.external_number, sc.check.external_url) + - safe_display(sc.quick_criterion) do + strong Kriterium + .body_text = _1 + - safe_display(sc.quick_fail) do + strong Quick Fail + .body_text = _1 + - safe_display(sc.quick_fix) do + strong Kriterium + .body_text = _1 + strong WCAG + .body_text = link_to(sc.check.external_number, sc.check.external_url) -nav - = link_to(@report.name, "##{dom_id(@report)}") - ul - li = link_to("Inhaltsverzeichnis", "#toc") - li - = link_to('Testbericht') - ul - - @report.pages.select { |p| p.elements.any? { |e| e.success_criteria.any?(&:failed?) } }.each do |page| - li - = link_to("#{page.position} #{page.path}", "##{dom_id(page)}") - ul - - page.elements.select { |e| e.success_criteria.any?(&:failed?) }.each do |element| - li - = link_to("#{element.number} #{element.title}") - ul - - element.success_criteria.select(&:failed?).each do |sc| - li = link_to("#{sc.number} #{sc.title}", "##{dom_id(sc)}") - li - = link_to("Anhang") - ul - - @failed_success_criteria.group_by(&:check).each do |check, scs| - li = link_to(check.display_label) +h2 3 Anhang - - -h2 Anhang - -h3 Liste der zu beachtenden WCAG Regeln +h3 3.1 Liste der zu beachtenden WCAG Regeln - @failed_success_criteria.group_by(&:check).sort_by{ |c, scs| c.number }.each do |check, scs| h4 = check.display_label - dl - - safe_display(check.external_number) do - dt WCAG Nummer - dd = _1 - - safe_display(check.external_url) do - dt WCAG Link - dd = _1 - - safe_display(check.conformity_level) do - dt Konformität - dd = _1 - - safe_display(check.conformity_notice_de) do - dt Anmerkung Konformität - dd = _1 - - safe_display(check.priority) do - dt Priorität - dd = _1 - - safe_display(check.criterion_de) do - dt Kriterium/Grundlage - dd = _1 - - safe_display(check.exemption_details_de) do - dt Ausnahmen - dd = _1 - - safe_display(check.criterion_details_de) do - dt Verstehen - dd = _1 - - safe_display(check.example_de) do - dt Beispiel - dd = _1 - - safe_display(check.annotation_de) do - dt Anmerkung - dd = _1 - - if check.links.any? - dt Links - dd - - check.links.group_by(&:category).each do |category, links| - strong = category - ul - - links.each do |l| - li = link_to(l.text, l.url) - dt Erfolgskriterien - dd = scs.map(&:number).join(", ") + - safe_display(check.external_number) do + strong WCAG Nummer + .body_text = _1 + - safe_display(check.external_url) do + strong WCAG Link + .body_text = _1 + - safe_display(check.conformity_level) do + strong Konformität + .body_text = _1 + - safe_display(check.conformity_notice_de) do + strong Anmerkung Konformität + .body_text = _1 + - safe_display(check.priority) do + strong Priorität + .body_text = _1 + - safe_display(check.criterion_de) do + strong Kriterium/Grundlage + .body_text = _1 + - safe_display(check.exemption_details_de) do + strong Ausnahmen + .body_text = _1 + - safe_display(check.criterion_details_de) do + strong Verstehen + .body_text = _1 + - safe_display(check.example_de) do + strong Beispiel + .body_text = _1 + - safe_display(check.annotation_de) do + strong Anmerkung + .body_text = _1 + - if check.links.any? + strong Links + .body_text + - check.links.group_by(&:category).each do |category, links| + strong = category + ul + - links.each do |l| + li = link_to(l.text, l.url) + strong Erfolgskriterien + .body_text = scs.map(&:number).join(", ") diff --git a/app/views/layouts/exports.html.slim b/app/views/layouts/exports.html.slim index 28f8a3a..469c94a 100644 --- a/app/views/layouts/exports.html.slim +++ b/app/views/layouts/exports.html.slim @@ -8,5 +8,5 @@ html data-bs-theme="light" data-controller="set-theme" = stylesheet_link_tag "exports", "data-turbo-track": "reload" = javascript_include_tag "application", "data-turbo-track": "reload", type: "module" body - main#main-content + main.container#main-content = yield From 0a487595760d75c0aef6db1925b83d5d1b8551f1 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 17 Nov 2024 02:44:04 +0100 Subject: [PATCH 12/46] Remove styles from export --- app/views/exports/show.html.slim | 21 +++++---------------- app/views/layouts/exports.html.slim | 4 +--- 2 files changed, 6 insertions(+), 19 deletions(-) diff --git a/app/views/exports/show.html.slim b/app/views/exports/show.html.slim index 4408d2b..269a527 100644 --- a/app/views/exports/show.html.slim +++ b/app/views/exports/show.html.slim @@ -1,13 +1,3 @@ -css: - .trix-content:has(*) { - font-family: "Serif"; - border: solid white 1px; - padding: 1rem; - } - dd { - margin-left: 10rem; - } - /nav = link_to(@report.name, "##{dom_id(@report)}") ul @@ -31,7 +21,7 @@ css: - @failed_success_criteria.group_by(&:check).each do |check, scs| li = link_to(check.display_label) -h1 id=dom_id(@report) = @report.name +h1.title id=dom_id(@report) = @report.name h2 1 Einschätzung p Gibt es hier Text? @@ -74,12 +64,11 @@ h2 2 Protokoll strong WCAG .body_text = link_to(sc.check.external_number, sc.check.external_url) -h2 3 Anhang - -h3 3.1 Liste der zu beachtenden WCAG Regeln - +h2 3 Anhang: Liste der zu beachtenden WCAG Regeln +- counter = 0 - @failed_success_criteria.group_by(&:check).sort_by{ |c, scs| c.number }.each do |check, scs| - h4 = check.display_label + - counter += 1 + h3 = "3.#{counter}. #{check.name_de}" - safe_display(check.external_number) do strong WCAG Nummer .body_text = _1 diff --git a/app/views/layouts/exports.html.slim b/app/views/layouts/exports.html.slim index 469c94a..92cda65 100644 --- a/app/views/layouts/exports.html.slim +++ b/app/views/layouts/exports.html.slim @@ -1,12 +1,10 @@ doctype html -html data-bs-theme="light" data-controller="set-theme" +html head title a11ydive Export meta[name="viewport" content="width=device-width,initial-scale=1"] = csrf_meta_tags = csp_meta_tag - = stylesheet_link_tag "exports", "data-turbo-track": "reload" - = javascript_include_tag "application", "data-turbo-track": "reload", type: "module" body main.container#main-content = yield From fa1d5b8bcef6f8c6e5534e20f47cfa29bd2c0eca Mon Sep 17 00:00:00 2001 From: david Date: Sun, 17 Nov 2024 13:44:51 +0100 Subject: [PATCH 13/46] Improve exports and GUI --- .../stylesheets/application.bootstrap.scss | 3 +- app/assets/stylesheets/dropdown.scss | 67 +++++++++++++++++++ app/assets/stylesheets/layout.scss | 28 ++++++-- app/helpers/application_helper.rb | 31 +++++++++ app/helpers/success_criteria_helper.rb | 20 +++++- app/views/exports/show.html.slim | 13 +++- app/views/success_criteria/_body.html.slim | 27 ++++---- app/views/success_criteria/_form.html.erb | 11 --- app/views/success_criteria/_form.html.slim | 9 +++ app/views/success_criteria/_header.html.slim | 4 +- .../success_criteria/_record_menu.html.slim | 3 + app/views/success_criteria/edit.html.erb | 10 --- app/views/success_criteria/edit.html.slim | 19 ++++++ 13 files changed, 195 insertions(+), 50 deletions(-) create mode 100644 app/assets/stylesheets/dropdown.scss delete mode 100644 app/views/success_criteria/_form.html.erb create mode 100644 app/views/success_criteria/_form.html.slim create mode 100644 app/views/success_criteria/_record_menu.html.slim delete mode 100644 app/views/success_criteria/edit.html.erb create mode 100644 app/views/success_criteria/edit.html.slim diff --git a/app/assets/stylesheets/application.bootstrap.scss b/app/assets/stylesheets/application.bootstrap.scss index 65faf19..976c954 100644 --- a/app/assets/stylesheets/application.bootstrap.scss +++ b/app/assets/stylesheets/application.bootstrap.scss @@ -162,4 +162,5 @@ trix-toolbar .trix-dialog--link { overflow-y: auto; } */ -@import "./layout"; \ No newline at end of file +@import "./layout"; +@import "./dropdown"; diff --git a/app/assets/stylesheets/dropdown.scss b/app/assets/stylesheets/dropdown.scss new file mode 100644 index 0000000..f751bfc --- /dev/null +++ b/app/assets/stylesheets/dropdown.scss @@ -0,0 +1,67 @@ +.details-dropdown { + display: inline-block; + position: relative; +} + +/* Hide The disclosure widget: */ +.details-dropdown summary { + list-style: none; + cursor: pointer; +} +.details-dropdown[open] summary { + list-style: none; + cursor: pointer; + i { + + color: var(--bs-body-color) + } + /* border-left: solid 1px $secondary; */ + /* border-top: solid 1px $secondary; */ + /* border-right: solid 1px $secondary; */ +} +.details-dropdown[open] { + background-color: $secondary; +} + +/* Detache details content */ +.details-dropdown .details-dropdown-content { + border: solid 1px $secondary; + position: absolute; + min-inline-size: max-content; + z-index: 2000; + + /* In case the details-dropdown should open to the left: */ + right: 0; +} + +/* Closing the details-dropdown on clicking anywhere else */ +.details-dropdown[open] summary::before { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + content: ""; + cursor: default; +} + +/* Visual styles for your details-dropdown: */ +.details-dropdown .details-dropdown-trigger { + /* your look and feel here */ +} + +.details-dropdown .details-dropdown-content { + /* your look and feel here */ +} + +form.no-padding { + button { + padding: 0; + } +} + +li.list-group-item-danger:hover { + button { + color: var(--bs-body-color) ; + } +} diff --git a/app/assets/stylesheets/layout.scss b/app/assets/stylesheets/layout.scss index 8700b9d..87fc2c1 100644 --- a/app/assets/stylesheets/layout.scss +++ b/app/assets/stylesheets/layout.scss @@ -91,7 +91,9 @@ $tertiary-dark-active-open: $gray-600; .page_nav nav ul { padding-left: 0; list-style-type: none; - line-height: 2rem; + li { + margin-bottom: 0.5rem; + } a { color: var(--bs-text); @@ -223,7 +225,14 @@ details.tree[open]>summary::before { padding-bottom: 0; } +details.success_criterion:last-child { + border-bottom: solid 1px $tertiary; +} + details.success_criterion { + border: solid 1px $tertiary; + border-bottom: solid 0px $tertiary; + summary:hover { background-color: $tertiary-hover; @@ -255,24 +264,31 @@ details.success_criterion { details.success_criterion[open] { border: solid 1px $tertiary; - summary { + >summary { background-color: $tertiary; } - summary:hover { + >summary:hover { background-color: $tertiary-active-hover; } } @include color-mode(dark) { + details.success_criterion { + border: solid 1px $tertiary-dark; + border-bottom: solid 0px $tertiary-dark; + } + details.success_criterion:last-child { + border-bottom: solid 1px $tertiary-dark; + } details.success_criterion[open] { border: solid 1px $tertiary-dark; - summary { + >summary { background-color: $tertiary-dark; } - summary:hover { + >summary:hover { background-color: $tertiary-dark-active-open; } } @@ -326,4 +342,4 @@ details[open] { .sortable-ghost { border: solid 3px $primary !important; -} \ No newline at end of file +} diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index c17a465..69e0cd5 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -32,4 +32,35 @@ module ApplicationHelper def turbo_stream_toast(content, alert) turbo_stream.append("toasts", partial: "layouts/toast", locals: { content:, alert: }) end + + + def dropdown_menu(items, klass: "") + tag.details(class: "details-dropdown #{klass}") do + tag.summary do + tag.div(class: "details-dropdown-trigger") do + tag.div(tag.i(class: "bi bi-three-dots-vertical"), class: "btn btn-outline-secondary") + end + end + + tag.div(class: "details-dropdown-content bg-secondary") do + tag.ul(class: "list-group") do + safe_join(items.map do |item| + tag.li(class: "list-group-item list-group-item-action#{ item[:color] ? " list-group-item-#{item[:color]}" : ""}") do + text = item[:icon] ? tag.i(class: "bi bi-#{item[:icon]} me-2") + " #{item[:text]}".html_safe : item[:text] + case item[:method] + when nil, :get + link_to(text, item[:href], class: "text-decoration-none text-body") + else + button_to(text, + item[:href], + method: item[:method], + form_class: "no-padding", + class: "btn btn-link text-decoration-none text-#{item[:color] ? "#{item[:color]}-emphasis" : "body"}", + data: { turbo_confirm: item[:confirm] }) + end + end + end) + end + end + end + end end diff --git a/app/helpers/success_criteria_helper.rb b/app/helpers/success_criteria_helper.rb index f15ebcc..5ecc7d0 100644 --- a/app/helpers/success_criteria_helper.rb +++ b/app/helpers/success_criteria_helper.rb @@ -25,8 +25,22 @@ module SuccessCriteriaHelper end end + def success_criterion_menu(success_criterion, show_mode = true) + dropdown_menu([ + { text: show_mode ? "Bearbeiten" : "Bearbeiten abbrechen", + icon: "pencil", + href: show_mode ? edit_success_criterion_path(success_criterion) : success_criterion_path(success_criterion)}, + { text: "Löschen", + icon: "trash", + href: success_criterion_path(success_criterion), + color: :danger, + method: :delete, + confirm: "Bist du sicher?"}], + klass: "mt-3 ms-auto") + end - def success_criterion_edit_button(success_criterion, edit_mode) + + def success_criterion_edit_button(success_criterion, edit_mode) path = if success_criterion.persisted? if edit_mode success_criterion @@ -37,9 +51,9 @@ module SuccessCriteriaHelper else success_criterion.element end - link_to tag.i(class: "bi bi-pencil"), + link_to tag.i(class: "bi bi-pencil") + " Bearbeiten".html_safe, path, - class: "btn btn-#{edit_mode ? 'link text-warning' : 'link text-body'}" + class: "text-decoration-none xbtn xbtn-#{edit_mode ? 'link text-warning' : 'link text-body'}" end def success_criterion_badge(content, extra_classes: "") diff --git a/app/views/exports/show.html.slim b/app/views/exports/show.html.slim index 269a527..b72b856 100644 --- a/app/views/exports/show.html.slim +++ b/app/views/exports/show.html.slim @@ -37,19 +37,26 @@ p Woher kommt dieser Text? h2 2 Protokoll - current_page_pos = 0 +- current_abs_element_pos = 0 - @report.pages.select { |p| p.elements.any? { |e| e.success_criteria.any? { _1.failed? } } }.each do |page| - current_page_pos += 1 - current_element_pos = 0 - h3 = "2.#{current_page_pos} #{page.path}" + - current_abs_element_pos = 0 + /h3 = "2.#{current_page_pos} #{page.path}" - page.elements { |e| e.success_criteria.any? { _1.failed? } }.each do |element| - current_element_pos += 1 + - current_abs_element_pos += 1 - current_sc_pos = 0 - h4 = "2.#{current_page_pos}.#{current_element_pos} #{element.title}" + /h4 = "2.#{current_page_pos}.#{current_element_pos} #{element.title}" + h3 = "2.#{current_abs_element_pos} #{element.title}" + p + strong Pfad: + span =< page.path = safe_display(element.screenshot) { image_tag(_1.representation(resize_to_fit: [250, 250]))} = element.description - element.success_criteria.select{ _1.failed? }.each do |sc| - current_sc_pos += 1 - h5 = "2.#{current_page_pos}.#{current_element_pos}.#{current_sc_pos} #{sc.title}" + h4 = "2.#{current_abs_element_pos}.#{current_sc_pos} #{sc.title}" - if sc.test_comment? p = sc.test_comment - safe_display(sc.quick_criterion) do diff --git a/app/views/success_criteria/_body.html.slim b/app/views/success_criteria/_body.html.slim index 59bf000..45a40d3 100644 --- a/app/views/success_criteria/_body.html.slim +++ b/app/views/success_criteria/_body.html.slim @@ -1,19 +1,18 @@ .content id="#{dom_id(success_criterion, :body)}" = turbo_frame_tag(dom_id(success_criterion, :frame)) do - .row - .col - .my-3.btn-group[role="group" aria-label="Resultat"] - = bootstrap_form_with(model: success_criterion, data: { controller: "autosubmit" }) do |form| - = form.radio_button_without_bootstrap :result, :passed, class: "btn-check", autocomplete: "off", id: dom_id(success_criterion, :result_passed) - label.btn.btn-outline-success for=dom_id(success_criterion, :result_passed) Bestanden - = form.radio_button_without_bootstrap :result, :failed, class: "btn-check", autocomplete: "off", id: dom_id(success_criterion, :result_failed) - label.btn.btn-outline-danger for=dom_id(success_criterion, :result_failed) Durchgefallen - = form.radio_button_without_bootstrap :result, :not_applicable, class: "btn-check", autocomplete: "off", id: dom_id(success_criterion, :result_not_applicable) - label.btn.btn-outline-secondary for=dom_id(success_criterion, :result_not_applicable) Nicht anwendbar - /= form.radio_button_without_bootstrap :result, nil, class: "btn-check", autocomplete: "off", id: dom_id(success_criterion, :result_not_applicable) - /label.btn.btn-outline-secondary for=dom_id(success_criterion, :nil) Reset - = button_to(tag.i(class: "bi bi-trash"), success_criterion, method: :delete, class: "btn btn-link text-danger", data: { turbo_confirm: "Bist du sicher?"}) - = success_criterion_edit_button(success_criterion, false) + .d-flex + .my-3.btn-group[role="group" aria-label="Resultat"] + = bootstrap_form_with(model: success_criterion, data: { controller: "autosubmit" }) do |form| + = form.radio_button_without_bootstrap :result, :passed, class: "btn-check", autocomplete: "off", id: dom_id(success_criterion, :result_passed) + label.btn.btn-outline-success for=dom_id(success_criterion, :result_passed) Bestanden + = form.radio_button_without_bootstrap :result, :failed, class: "btn-check", autocomplete: "off", id: dom_id(success_criterion, :result_failed) + label.btn.btn-outline-danger for=dom_id(success_criterion, :result_failed) Durchgefallen + = form.radio_button_without_bootstrap :result, :not_applicable, class: "btn-check", autocomplete: "off", id: dom_id(success_criterion, :result_not_applicable) + label.btn.btn-outline-secondary for=dom_id(success_criterion, :result_not_applicable) Nicht anwendbar + /= form.radio_button_without_bootstrap :result, nil, class: "btn-check", autocomplete: "off", id: dom_id(success_criterion, :result_not_applicable) + /label.btn.btn-outline-secondary for=dom_id(success_criterion, :nil) Reset + / = dropdown_menu([{ text: "Bearbeiten", icon: "pencil", href: edit_success_criterion_path(success_criterion) }, { text: "Löschen", icon: "trash", href: success_criterion, color: :danger, method: :delete, confirm: "Bist du sicher?"}], klass: "mt-3 ms-auto") + = success_criterion_menu(success_criterion) .row .col - if success_criterion.test_comment? diff --git a/app/views/success_criteria/_form.html.erb b/app/views/success_criteria/_form.html.erb deleted file mode 100644 index 8f351a1..0000000 --- a/app/views/success_criteria/_form.html.erb +++ /dev/null @@ -1,11 +0,0 @@ - - <%= bootstrap_form_with(model: success_criterion.persisted? ? success_criterion : [:element, success_criterion], data: { controller: "unsaved-changes" }) do |form| %> - <%= form.text_field :title %> - <%= form.collection_select :result, SuccessCriterion.results.keys.map { [_1, t("activerecord.attributes.success_criterion.results/#{_1}")] }, :first, :second, include_blank: success_criterion.result ? "(Resultat zurücksetzen)" : "(unbeantwortet)" %> - <%= form.rich_text_area :quick_criterion %> - <%= form.rich_text_area :quick_fail %> - <%= form.rich_text_area :quick_fix %> - <%= form.rich_text_area :test_comment %> - <%= form.submit class: "btn btn-primary" %> - <%= link_to "Abbrechen", success_criterion.persisted? ? success_criterion : success_criterion.element, class: "btn btn-outline-secondary" %> - <% end %> diff --git a/app/views/success_criteria/_form.html.slim b/app/views/success_criteria/_form.html.slim new file mode 100644 index 0000000..fe12bf5 --- /dev/null +++ b/app/views/success_criteria/_form.html.slim @@ -0,0 +1,9 @@ += bootstrap_form_with(model: success_criterion.persisted? ? success_criterion : [:element, success_criterion], data: { controller: "unsaved-changes" }) do |form| + = form.text_field :title + = form.collection_select :result, SuccessCriterion.results.keys.map { [_1, t("activerecord.attributes.success_criterion.results/#{_1}")] }, :first, :second, include_blank: success_criterion.result ? "(Resultat zurücksetzen)" : "(unbeantwortet)" + = form.rich_text_area :quick_criterion + = form.rich_text_area :quick_fail + = form.rich_text_area :quick_fix + = form.rich_text_area :test_comment + = form.submit class: "btn btn-primary" + = link_to "Abbrechen", success_criterion.persisted? ? success_criterion : success_criterion.element, class: "btn btn-outline-secondary" diff --git a/app/views/success_criteria/_header.html.slim b/app/views/success_criteria/_header.html.slim index a27c7be..db31ae4 100644 --- a/app/views/success_criteria/_header.html.slim +++ b/app/views/success_criteria/_header.html.slim @@ -3,7 +3,7 @@ summary.d-flex.align-items-start id=dom_id(success_criterion, :header) .content.d-flex.align-items-center.w-100 .result-icon.flex-shrink-0 class=[success_criterion_result_color_classes(success_criterion)] span.h1.bi class=[success_criterion_result_icon_classes(success_criterion)] - .flex-fill + .flex-fill.py-1 span id=dom_id(success_criterion, :position) = success_criterion.page.position | . @@ -19,4 +19,4 @@ summary.d-flex.align-items-start id=dom_id(success_criterion, :header) = success_criterion_badge(success_criterion.check.external_number, extra_classes: "text-bg-info me-1") = success_criterion_badge(success_criterion.level, extra_classes: "sc-level-#{success_criterion.level.to_s.downcase} me-1") - i.bi.bi-grip-vertical.handle \ No newline at end of file + i.bi.bi-grip-vertical.handle diff --git a/app/views/success_criteria/_record_menu.html.slim b/app/views/success_criteria/_record_menu.html.slim new file mode 100644 index 0000000..5524b70 --- /dev/null +++ b/app/views/success_criteria/_record_menu.html.slim @@ -0,0 +1,3 @@ + = dropdown_menu(klass: "mt-3 ms-auto b-0") do + = success_criterion_edit_button(@success_criterion, true) + = button_to(tag.i(class: "bi bi-trash") + " Löschen".html_safe, @success_criterion, method: :delete, class: "text-danger", data: { turbo_confirm: "Bist du sicher?"}) diff --git a/app/views/success_criteria/edit.html.erb b/app/views/success_criteria/edit.html.erb deleted file mode 100644 index f0093e8..0000000 --- a/app/views/success_criteria/edit.html.erb +++ /dev/null @@ -1,10 +0,0 @@ -

<%= t("scaffold.pagetitle_edit", model: SuccessCriterion.model_name.human) %>

- -<%= turbo_frame_tag(dom_id(@success_criterion, :frame)) do %> - <%= render "form", success_criterion: @success_criterion %> -<% end %> - -
- <%= link_to t("scaffold.link_show", model: SuccessCriterion.model_name.human), @success_criterion %> - <%= link_to t("scaffold.link_index", model: SuccessCriterion.model_name.human(count: 2)), element_success_criteria_path(@success_criterion.element) %> -
diff --git a/app/views/success_criteria/edit.html.slim b/app/views/success_criteria/edit.html.slim new file mode 100644 index 0000000..d3c144a --- /dev/null +++ b/app/views/success_criteria/edit.html.slim @@ -0,0 +1,19 @@ +h1 + = t("scaffold.pagetitle_edit", model: SuccessCriterion.model_name.human) += turbo_frame_tag(dom_id(@success_criterion, :frame)) do + .d-flex + h2.my-2 Bearbeiten + .ms-auto + = success_criterion_menu(@success_criterion, false) + /= dropdown_menu(klass: "mt-3 b-0") do + ul.list-group + li.list-group-item + = success_criterion_edit_button(@success_criterion, true) + li.list-group-item + = button_to(tag.i(class: "bi bi-trash") + " Löschen".html_safe, @success_criterion, method: :delete, class: "btn text-danger", data: { turbo_confirm: "Bist du sicher?"}) + + .mb-3 + = render "form", success_criterion: @success_criterion +.action-row + = link_to t("scaffold.link_show", model: SuccessCriterion.model_name.human), @success_criterion + = link_to t("scaffold.link_index", model: SuccessCriterion.model_name.human(count: 2)), element_success_criteria_path(@success_criterion.element) From 10820119413ca31363060c108c96a42a773d3a9b Mon Sep 17 00:00:00 2001 From: david Date: Sun, 17 Nov 2024 13:48:05 +0100 Subject: [PATCH 14/46] fix attribute name --- app/views/exports/show.html.slim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/exports/show.html.slim b/app/views/exports/show.html.slim index b72b856..a609e6c 100644 --- a/app/views/exports/show.html.slim +++ b/app/views/exports/show.html.slim @@ -109,7 +109,7 @@ h2 3 Anhang: Liste der zu beachtenden WCAG Regeln - if check.links.any? strong Links .body_text - - check.links.group_by(&:category).each do |category, links| + - check.links.group_by(&:link_category).each do |category, links| strong = category ul - links.each do |l| From 096e541969a213c733a6c775146cae885c1ad47d Mon Sep 17 00:00:00 2001 From: david Date: Sun, 17 Nov 2024 13:53:40 +0100 Subject: [PATCH 15/46] Fix last fix........ --- app/views/exports/show.html.slim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/exports/show.html.slim b/app/views/exports/show.html.slim index a609e6c..b1329b9 100644 --- a/app/views/exports/show.html.slim +++ b/app/views/exports/show.html.slim @@ -110,7 +110,7 @@ h2 3 Anhang: Liste der zu beachtenden WCAG Regeln strong Links .body_text - check.links.group_by(&:link_category).each do |category, links| - strong = category + strong = category.t_name ul - links.each do |l| li = link_to(l.text, l.url) From 92c7fd43b102001b6a4605f51a69daf84f395010 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 17 Nov 2024 17:07:54 +0100 Subject: [PATCH 16/46] Fix number in export --- app/views/exports/show.html.slim | 1 - 1 file changed, 1 deletion(-) diff --git a/app/views/exports/show.html.slim b/app/views/exports/show.html.slim index b1329b9..7f24b9a 100644 --- a/app/views/exports/show.html.slim +++ b/app/views/exports/show.html.slim @@ -41,7 +41,6 @@ h2 2 Protokoll - @report.pages.select { |p| p.elements.any? { |e| e.success_criteria.any? { _1.failed? } } }.each do |page| - current_page_pos += 1 - current_element_pos = 0 - - current_abs_element_pos = 0 /h3 = "2.#{current_page_pos} #{page.path}" - page.elements { |e| e.success_criteria.any? { _1.failed? } }.each do |element| - current_element_pos += 1 From b400ba7cea1b54885e70e634efd4704f9cec5b2b Mon Sep 17 00:00:00 2001 From: david Date: Sun, 17 Nov 2024 17:11:17 +0100 Subject: [PATCH 17/46] show check number and select correct elements --- app/views/exports/show.html.slim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/views/exports/show.html.slim b/app/views/exports/show.html.slim index 7f24b9a..2ec2169 100644 --- a/app/views/exports/show.html.slim +++ b/app/views/exports/show.html.slim @@ -42,7 +42,7 @@ h2 2 Protokoll - current_page_pos += 1 - current_element_pos = 0 /h3 = "2.#{current_page_pos} #{page.path}" - - page.elements { |e| e.success_criteria.any? { _1.failed? } }.each do |element| + - page.elements.select { |e| e.success_criteria.any? { _1.failed? } }.each do |element| - current_element_pos += 1 - current_abs_element_pos += 1 - current_sc_pos = 0 @@ -56,6 +56,7 @@ h2 2 Protokoll - element.success_criteria.select{ _1.failed? }.each do |sc| - current_sc_pos += 1 h4 = "2.#{current_abs_element_pos}.#{current_sc_pos} #{sc.title}" + small = sc.number - if sc.test_comment? p = sc.test_comment - safe_display(sc.quick_criterion) do From acc5357627274495be6a43a556d6ded798f7ce75 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 17 Nov 2024 18:57:33 +0100 Subject: [PATCH 18/46] Font color on danger menu item --- app/assets/stylesheets/dropdown.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/stylesheets/dropdown.scss b/app/assets/stylesheets/dropdown.scss index f751bfc..b8bb038 100644 --- a/app/assets/stylesheets/dropdown.scss +++ b/app/assets/stylesheets/dropdown.scss @@ -62,6 +62,6 @@ form.no-padding { li.list-group-item-danger:hover { button { - color: var(--bs-body-color) ; + color: var(--bs-body-color) !important; } } From e576aa1e390018a9957d3b4a7c5b8376315a93aa Mon Sep 17 00:00:00 2001 From: david Date: Sun, 17 Nov 2024 18:57:44 +0100 Subject: [PATCH 19/46] Add page to hierarchy --- app/views/exports/show.html.slim | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/app/views/exports/show.html.slim b/app/views/exports/show.html.slim index 2ec2169..e925508 100644 --- a/app/views/exports/show.html.slim +++ b/app/views/exports/show.html.slim @@ -41,13 +41,13 @@ h2 2 Protokoll - @report.pages.select { |p| p.elements.any? { |e| e.success_criteria.any? { _1.failed? } } }.each do |page| - current_page_pos += 1 - current_element_pos = 0 - /h3 = "2.#{current_page_pos} #{page.path}" + h3 = "2.#{current_page_pos} #{page.path}" - page.elements.select { |e| e.success_criteria.any? { _1.failed? } }.each do |element| - current_element_pos += 1 - current_abs_element_pos += 1 - current_sc_pos = 0 - /h4 = "2.#{current_page_pos}.#{current_element_pos} #{element.title}" - h3 = "2.#{current_abs_element_pos} #{element.title}" + h4 = "2.#{current_page_pos}.#{current_element_pos} #{element.title}" + /h3 = "2.#{current_abs_element_pos} #{element.title}" p strong Pfad: span =< page.path @@ -55,8 +55,11 @@ h2 2 Protokoll = element.description - element.success_criteria.select{ _1.failed? }.each do |sc| - current_sc_pos += 1 - h4 = "2.#{current_abs_element_pos}.#{current_sc_pos} #{sc.title}" - small = sc.number + /h4 + = "2.#{current_abs_element_pos}.#{current_sc_pos} #{sc.title}" + h5 = "2.#{current_page_pos}.#{current_element_pos}.#{current_sc_pos}" + strong Protokoll-Nummer + =< sc.number - if sc.test_comment? p = sc.test_comment - safe_display(sc.quick_criterion) do @@ -66,7 +69,7 @@ h2 2 Protokoll strong Quick Fail .body_text = _1 - safe_display(sc.quick_fix) do - strong Kriterium + strong Quick Fix .body_text = _1 strong WCAG .body_text = link_to(sc.check.external_number, sc.check.external_url) From bf03407bb91bb121ce66ff027a45dd2db0e02a7c Mon Sep 17 00:00:00 2001 From: david Date: Sun, 17 Nov 2024 19:00:52 +0100 Subject: [PATCH 20/46] more beautiful button and export --- app/helpers/application_helper.rb | 26 ++++++++++++-------------- app/views/exports/show.html.slim | 5 +++-- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 69e0cd5..3c87ca1 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -42,21 +42,19 @@ module ApplicationHelper end end + tag.div(class: "details-dropdown-content bg-secondary") do - tag.ul(class: "list-group") do + tag.div(class: "list-group") do safe_join(items.map do |item| - tag.li(class: "list-group-item list-group-item-action#{ item[:color] ? " list-group-item-#{item[:color]}" : ""}") do - text = item[:icon] ? tag.i(class: "bi bi-#{item[:icon]} me-2") + " #{item[:text]}".html_safe : item[:text] - case item[:method] - when nil, :get - link_to(text, item[:href], class: "text-decoration-none text-body") - else - button_to(text, - item[:href], - method: item[:method], - form_class: "no-padding", - class: "btn btn-link text-decoration-none text-#{item[:color] ? "#{item[:color]}-emphasis" : "body"}", - data: { turbo_confirm: item[:confirm] }) - end + c = "list-group-item list-group-item-action #{ item[:color] ? " list-group-item-#{item[:color]}" : "list-group-item-secondary"}" + text = item[:icon] ? tag.i(class: "bi bi-#{item[:icon]} me-2") + " #{item[:text]}".html_safe : item[:text] + case item[:method] + when nil, :get + link_to(text, item[:href], class: "#{c}") + else + button_to(text, + item[:href], + method: item[:method], + class: "#{c}", + data: { turbo_confirm: item[:confirm] }) end end) end diff --git a/app/views/exports/show.html.slim b/app/views/exports/show.html.slim index e925508..541600e 100644 --- a/app/views/exports/show.html.slim +++ b/app/views/exports/show.html.slim @@ -58,8 +58,9 @@ h2 2 Protokoll /h4 = "2.#{current_abs_element_pos}.#{current_sc_pos} #{sc.title}" h5 = "2.#{current_page_pos}.#{current_element_pos}.#{current_sc_pos}" - strong Protokoll-Nummer - =< sc.number + p + strong Protokoll-Nummer + =< sc.number - if sc.test_comment? p = sc.test_comment - safe_display(sc.quick_criterion) do From 889ad3275b96596a1b76058836ae68a4a46dbca4 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 17 Nov 2024 19:02:59 +0100 Subject: [PATCH 21/46] Fix title --- app/views/exports/show.html.slim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/exports/show.html.slim b/app/views/exports/show.html.slim index 541600e..d7ca86d 100644 --- a/app/views/exports/show.html.slim +++ b/app/views/exports/show.html.slim @@ -57,7 +57,7 @@ h2 2 Protokoll - current_sc_pos += 1 /h4 = "2.#{current_abs_element_pos}.#{current_sc_pos} #{sc.title}" - h5 = "2.#{current_page_pos}.#{current_element_pos}.#{current_sc_pos}" + h5 = "2.#{current_page_pos}.#{current_element_pos}.#{current_sc_pos} #{sc.title}" p strong Protokoll-Nummer =< sc.number From 7b0f05a448be00c7e50c25372e2c0503f67b79e1 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 17 Nov 2024 19:37:02 +0100 Subject: [PATCH 22/46] fix external number params --- app/controllers/checks_controller.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/checks_controller.rb b/app/controllers/checks_controller.rb index 551e7c2..303415e 100644 --- a/app/controllers/checks_controller.rb +++ b/app/controllers/checks_controller.rb @@ -95,7 +95,9 @@ class ChecksController < ApplicationController :applicable_to_analogue, :applicable_to_document, :applicable_to_non_web, - :external_number, + :external_number_1, + :external_number_2, + :external_number_3, :external_url, :conformity_level, :conformity_notice_de, From 70500c49a12bffddc798144c4b19f913cd356a10 Mon Sep 17 00:00:00 2001 From: david Date: Sat, 23 Nov 2024 19:10:09 +0100 Subject: [PATCH 23/46] Model menus, modal edit and layout improvements --- Dockerfile | 50 +- app/assets/stylesheets/layout.scss | 110 +++-- app/controllers/application_controller.rb | 4 + app/controllers/elements_controller.rb | 4 +- app/controllers/pages_controller.rb | 1 + .../success_criteria_controller.rb | 4 +- app/helpers/application_helper.rb | 10 +- app/helpers/elements_helper.rb | 15 + app/helpers/pages_helper.rb | 25 + app/helpers/success_criteria_helper.rb | 23 +- .../controllers/details_list_controller.js | 4 +- .../controllers/dialog_controller.js | 53 ++ .../controllers/dropdown_menu_controller.js | 16 + app/javascript/controllers/index.js | 9 +- app/models/success_criterion.rb | 2 + .../destroy.turbo_stream.erb | 3 +- app/views/elements/_element.html.erb | 12 +- app/views/elements/destroy.turbo_stream.slim | 2 +- app/views/elements/edit.html.erb | 12 +- app/views/layouts/_toast.html.slim | 2 +- app/views/layouts/application.html.slim | 5 +- app/views/layouts/modal.html.slim | 9 + app/views/pages/_page.html.erb | 2 +- app/views/pages/edit.html.erb | 2 + app/views/pages/show.html.slim | 9 +- app/views/reports/show.html.slim | 47 +- app/views/success_criteria/_body.html.slim | 2 +- app/views/success_criteria/_form.html.slim | 3 +- app/views/success_criteria/_header.html.slim | 2 +- .../destroy.turbo_stream.slim | 4 +- app/views/success_criteria/edit.html.slim | 26 +- .../success_criteria/update.turbo_stream.slim | 3 +- package-lock.json | 455 +++++++++++++++++- package.json | 4 +- yarn.lock | 293 ++++++++++- 35 files changed, 1079 insertions(+), 148 deletions(-) create mode 100644 app/javascript/controllers/dialog_controller.js create mode 100644 app/javascript/controllers/dropdown_menu_controller.js create mode 100644 app/views/layouts/modal.html.slim diff --git a/Dockerfile b/Dockerfile index 6ae5155..0e930c6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,9 @@ ENV \ RAILS_ENV=development \ TZ=Europe/Zurich \ PATH=${INSTALL_DIR}/bin:$GEM_HOME/bin:$GEM_HOME/gems/bin:$PATH \ - EDITOR=vim + EDITOR=vim \ + RUBY_DEBUG_DAP_SHOW_PROTOCOL=1 \ + RUBY_DEBUG_OPEN=true RUN \ ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \ @@ -49,7 +51,11 @@ RUN \ apt-get install -yqq --no-install-recommends \ sqlite3 nodejs npm sassc yarn libvips fish ranger pandoc libjemalloc2 && \ apt-get clean && \ - npm install tabby-agent && \ + npm i -g tabby-agent \ + vscode-langservers-extracted \ + dockerfile-language-server-nodejs \ + @microsoft/compose-language-service \ + yaml-language-server@next && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \ truncate -s 0 /var/log/*log && \ gem update --system && \ @@ -62,8 +68,6 @@ RUN curl -L https://github.com/helix-editor/helix/releases/download/${HELIX_VERS tar -xf /tmp/helix.tar.xz && \ ln -s /opt/helix-${HELIX_VERSION}-x86_64-linux/hx /usr/local/bin chmod +x /usr/local/bin/ -USER ${NAME} - RUN mkdir -p ~/.config/fish ~/.config/helix COPY <.content { padding-left: 1rem; padding-right: 1rem; + padding-bottom: 1rem; } } @@ -262,7 +234,7 @@ details.success_criterion { details.success_criterion[open] { - border: solid 1px $tertiary; + /* border: solid 1px $tertiary; */ >summary { background-color: $tertiary; @@ -275,14 +247,17 @@ details.success_criterion[open] { @include color-mode(dark) { details.success_criterion { - border: solid 1px $tertiary-dark; - border-bottom: solid 0px $tertiary-dark; + border-left: solid 1px $tertiary-dark; + border-right: solid 1px $tertiary-dark; + border-top: solid 1px $tertiary-dark; } + details.success_criterion:last-child { border-bottom: solid 1px $tertiary-dark; } + details.success_criterion[open] { - border: solid 1px $tertiary-dark; + /* border: solid 1px $tertiary-dark; */ >summary { background-color: $tertiary-dark; @@ -343,3 +318,64 @@ details[open] { .sortable-ghost { border: solid 3px $primary !important; } + +$dialog-animation-time: 0.5s; +$dialog-animation-start-state: scaleX(1); +$dialog-animation-end-state: scaleX(1); +/* dialog::backdrop { */ +/* background-color: white; */ +/* opacity: 0.75; */ +/* } */ + +/* Open state of the dialog */ +dialog[open] { + opacity: 1; + transform: $dialog-animation-end-state; +} + +/* Closed state of the dialog */ +dialog { + opacity: 0; + transform: $dialog-animation-start-state; + transition: + opacity $dialog-animation-time ease-out, + transform $dialog-animation-time ease-out, + overlay $dialog-animation-time ease-out allow-discrete, + display $dialog-animation-time ease-out allow-discrete; + /* Equivalent to + transition: all $dialog-animation-time allow-discrete; */ +} + +/* Before-open state */ +/* Needs to be after the previous dialog[open] rule to take effect, + as the specificity is the same */ +@starting-style { + dialog[open] { + opacity: 0; + transform: $dialog-animation-start-state; + } +} + +/* Transition the :backdrop when the dialog modal is promoted to the top layer */ +dialog::backdrop { + background-color: rgb(0 0 0 / 75%); + transition: + display $dialog-animation-time allow-discrete, + overlay $dialog-animation-time allow-discrete, + background-color $dialog-animation-time; + /* Equivalent to + transition: all $dialog-animation-time allow-discrete; */ +} + +dialog[open]::backdrop { + background-color: rgb(0 0 0 / 75%); +} + +/* This starting-style rule cannot be nested inside the above selector +because the nesting selector cannot represent pseudo-elements. */ + +@starting-style { + dialog[open]::backdrop { + background-color: rgb(0 0 0 / 0%); + } +} \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3804ad6..9c0c681 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -50,4 +50,8 @@ class ApplicationController < ActionController::Base def initialize_sidebar_items [] end + + def render_modal(action: action_name) + render action, layout: "modal" if turbo_frame_request_id == "modal" + end end diff --git a/app/controllers/elements_controller.rb b/app/controllers/elements_controller.rb index abfd931..fd94e84 100644 --- a/app/controllers/elements_controller.rb +++ b/app/controllers/elements_controller.rb @@ -19,7 +19,9 @@ class ElementsController < ApplicationController end # GET /elements/1/edit - def edit; end + def edit + render_modal + end # POST /elements def create diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index 13f2fb8..c73d631 100644 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -19,6 +19,7 @@ class PagesController < ApplicationController # GET /pages/1/edit def edit + render_modal end # POST /pages diff --git a/app/controllers/success_criteria_controller.rb b/app/controllers/success_criteria_controller.rb index 4ae7840..f77a931 100644 --- a/app/controllers/success_criteria_controller.rb +++ b/app/controllers/success_criteria_controller.rb @@ -18,7 +18,9 @@ class SuccessCriteriaController < ApplicationController end # GET /success_criteria/1/edit - def edit; end + def edit + render_modal() + end # POST /success_criteria def create diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 3c87ca1..437f6fc 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -35,10 +35,10 @@ module ApplicationHelper def dropdown_menu(items, klass: "") - tag.details(class: "details-dropdown #{klass}") do + tag.details(class: "details-dropdown #{klass}", data: { controller: "dropdown-menu" }) do tag.summary do tag.div(class: "details-dropdown-trigger") do - tag.div(tag.i(class: "bi bi-three-dots-vertical"), class: "btn btn-outline-secondary") + tag.div(tag.i(class: "bi bi-three-dots"), class: "btn btn-outline-body") end end + tag.div(class: "details-dropdown-content bg-secondary") do @@ -48,7 +48,7 @@ module ApplicationHelper text = item[:icon] ? tag.i(class: "bi bi-#{item[:icon]} me-2") + " #{item[:text]}".html_safe : item[:text] case item[:method] when nil, :get - link_to(text, item[:href], class: "#{c}") + link_to(text, item[:href], class: "#{c}", data: { turbo_frame: item[:turbo_frame], action: item[:action] }) else button_to(text, item[:href], @@ -61,4 +61,8 @@ module ApplicationHelper end end end + + def modal? + turbo_frame_request_id == "modal" + end end diff --git a/app/helpers/elements_helper.rb b/app/helpers/elements_helper.rb index d7f10ce..1488e59 100644 --- a/app/helpers/elements_helper.rb +++ b/app/helpers/elements_helper.rb @@ -1,4 +1,19 @@ # frozen_string_literal: true module ElementsHelper + def element_menu(element) + dropdown_menu([ + { text: "Bearbeiten", + icon: "pencil", + href: edit_element_path(element), + turbo_frame: "modal", + color: "body" }, + { text: "Löschen", + icon: "trash", + href: element_path(element), + color: :danger, + method: :delete, + confirm: "Bist du sicher?" } ], + klass: "ms-auto") + end end diff --git a/app/helpers/pages_helper.rb b/app/helpers/pages_helper.rb index 2c057fd..6aa2874 100644 --- a/app/helpers/pages_helper.rb +++ b/app/helpers/pages_helper.rb @@ -1,2 +1,27 @@ module PagesHelper + def page_menu(page) + dropdown_menu([ + { + text: "Alle zu [s]", + href: "#", + action: "click->details-list#closeAll" + }, + { + text: "Alle auf [a]", + href: "#", + action: "click->details-list#openAll" + }, + { text: "Bearbeiten", + icon: "pencil", + href: edit_page_path(page), + turbo_frame: "modal", + color: "body" }, + { text: "Löschen", + icon: "trash", + href: page_path(page), + color: :danger, + method: :delete, + confirm: "Bist du sicher?" } ], + klass: "ms-auto") + end end diff --git a/app/helpers/success_criteria_helper.rb b/app/helpers/success_criteria_helper.rb index 5ecc7d0..4b786fe 100644 --- a/app/helpers/success_criteria_helper.rb +++ b/app/helpers/success_criteria_helper.rb @@ -27,19 +27,20 @@ module SuccessCriteriaHelper def success_criterion_menu(success_criterion, show_mode = true) dropdown_menu([ - { text: show_mode ? "Bearbeiten" : "Bearbeiten abbrechen", - icon: "pencil", - href: show_mode ? edit_success_criterion_path(success_criterion) : success_criterion_path(success_criterion)}, - { text: "Löschen", - icon: "trash", - href: success_criterion_path(success_criterion), - color: :danger, - method: :delete, - confirm: "Bist du sicher?"}], - klass: "mt-3 ms-auto") + { text: "Bearbeiten", + icon: "pencil", + href: edit_success_criterion_path(success_criterion), + turbo_frame: "modal", + color: "body" }, + { text: "Löschen", + icon: "trash", + href: success_criterion_path(success_criterion), + color: :danger, + method: :delete, + confirm: "Bist du sicher?" } ], + klass: "mt-3 ms-auto") end - def success_criterion_edit_button(success_criterion, edit_mode) path = if success_criterion.persisted? if edit_mode diff --git a/app/javascript/controllers/details_list_controller.js b/app/javascript/controllers/details_list_controller.js index d521c4c..29a265a 100644 --- a/app/javascript/controllers/details_list_controller.js +++ b/app/javascript/controllers/details_list_controller.js @@ -10,7 +10,7 @@ export default class extends Controller { e.preventDefault(); const id = this.element.dataset["targetId"] const el = document.getElementById(id) - el.querySelectorAll("details").forEach(el => { + el.querySelectorAll("details.success_criterion").forEach(el => { el.setAttribute("open", "") }) } @@ -19,7 +19,7 @@ export default class extends Controller { e.preventDefault(); const id = this.element.dataset["targetId"] const el = document.getElementById(id) - el.querySelectorAll("details").forEach(el => { + el.querySelectorAll("details.success_criterion").forEach(el => { el.removeAttribute("open") }) } diff --git a/app/javascript/controllers/dialog_controller.js b/app/javascript/controllers/dialog_controller.js new file mode 100644 index 0000000..4dabac7 --- /dev/null +++ b/app/javascript/controllers/dialog_controller.js @@ -0,0 +1,53 @@ +// app/javascript/controllers/dialog_controller.js +import { Controller } from "@hotwired/stimulus" + +// Connects to data-controller="dialog" +export default class extends Controller { + connect() { + this.open() + console.log("connect dialog", this.element) + this.element.addEventListener("turbo:submit-end", e => { + this.submitEnd(e) + }) + this.element.addEventListener("click", e => this.clickOutside(e)) + } + + disconnect() { + } + + // hide modal on successful form submission + // data-action="turbo:submit-end->turbo-modal#submitEnd" + submitEnd(e) { + if (e.detail.success) { + this.close() + } + } + + open() { + console.log("open dialog") + this.element.showModal() + document.body.classList.add('overflow-hidden') + this.element.addEventListener("close", this.enableBodyScroll.bind(this)) + } + + close() { + this.element.removeEventListener("close", this.enableBodyScroll.bind(this)) + this.element.close() + // clean up modal content + const frame = document.getElementById('modal') + frame.removeAttribute("src") + frame.innerHTML = "" + } + + enableBodyScroll() { + document.body.classList.remove('overflow-hidden') + } + + clickOutside(event) { + console.log("clickOutside", event.target, this) + if (event.target === this.element) { + this.close() + } + } +} + diff --git a/app/javascript/controllers/dropdown_menu_controller.js b/app/javascript/controllers/dropdown_menu_controller.js new file mode 100644 index 0000000..adbbec8 --- /dev/null +++ b/app/javascript/controllers/dropdown_menu_controller.js @@ -0,0 +1,16 @@ +import { Controller } from "@hotwired/stimulus" + +// Connects to data-controller="dropdown-menu" +export default class extends Controller { + connect() { + const x = this.element + this.element.addEventListener("turbo:click", e => { + console.log("turbo visit dropdown", e, this.element) + this.element.removeAttribute("open"); + }) + this.element.addEventListener("turbo:submit-start", e => { + console.log("turbo submit dropdown", e, this.element) + this.element.removeAttribute("open"); + }) + } +} diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index 8654ab4..6ad9f94 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -19,9 +19,15 @@ application.register("collapse-chevron-toggler", CollapseChevronTogglerControlle import DetailsListController from "./details_list_controller" application.register("details-list", DetailsListController) +import DialogController from "./dialog_controller" +application.register("dialog", DialogController) + import DragController from "./drag_controller" application.register("drag", DragController) +import DropdownMenuController from "./dropdown_menu_controller" +application.register("dropdown-menu", DropdownMenuController) + import HelloController from "./hello_controller" application.register("hello", HelloController) @@ -48,6 +54,3 @@ application.register("toast", ToastController) import UnsavedChangesController from "./unsaved_changes_controller" application.register("unsaved-changes", UnsavedChangesController) - -import Lightbox from '@stimulus-components/lightbox' -application.register('lightbox', Lightbox) \ No newline at end of file diff --git a/app/models/success_criterion.rb b/app/models/success_criterion.rb index 03c97fa..e575308 100644 --- a/app/models/success_criterion.rb +++ b/app/models/success_criterion.rb @@ -17,6 +17,8 @@ class SuccessCriterion < ApplicationRecord before_save :set_position before_update :update_positions, if: :position_changed? + validates :result, inclusion: { in: self.results.keys + [ nil ] } + def level_value return nil unless level diff --git a/app/views/checklist_entries/destroy.turbo_stream.erb b/app/views/checklist_entries/destroy.turbo_stream.erb index 6e7c01a..4b1f257 100644 --- a/app/views/checklist_entries/destroy.turbo_stream.erb +++ b/app/views/checklist_entries/destroy.turbo_stream.erb @@ -1 +1,2 @@ -<%= turbo_stream.remove dom_id(@checklist_entry) %> \ No newline at end of file +<%= turbo_stream.remove dom_id(@checklist_entry) %> +<%= turbo_stream_toast("Check wurde aus Checkliste entfernt", true) %> diff --git a/app/views/elements/_element.html.erb b/app/views/elements/_element.html.erb index 1571085..0c92ffc 100644 --- a/app/views/elements/_element.html.erb +++ b/app/views/elements/_element.html.erb @@ -1,6 +1,6 @@
<%= turbo_frame_tag dom_id(element, :frame) do %> -
+

@@ -9,13 +9,7 @@ <%= element.title %>

- <%= link_to [:edit, element], class: "btn btn-link text-secondary" do %> - - - <% end %> - <%= button_to(element_path(element), method: :delete, class: "btn btn-link text-danger", data: { turbo_confirm: "Bist du sicher?"}) do %> - - <% end %> + <%= element_menu(element) %>
@@ -28,7 +22,7 @@
<%= link_to(s) do %> <%= image_tag(s.variant(:thumbnail), class: "img-fluid", alt: "Screenshot des getesteten Elements") %> - <% end %> + <% end rescue nil %>
<% end %>
diff --git a/app/views/elements/destroy.turbo_stream.slim b/app/views/elements/destroy.turbo_stream.slim index 35f04de..5305209 100644 --- a/app/views/elements/destroy.turbo_stream.slim +++ b/app/views/elements/destroy.turbo_stream.slim @@ -5,4 +5,4 @@ = turbo_stream.replace dom_id(e, :page_nav_row), partial: "elements/page_nav_row", locals: { element: e, current_page: true } -= turbo_stream_toast("Element wurde gelöscht", false) \ No newline at end of file += turbo_stream_toast("Element wurde gelöscht", true) diff --git a/app/views/elements/edit.html.erb b/app/views/elements/edit.html.erb index ade1f43..f1e84ae 100644 --- a/app/views/elements/edit.html.erb +++ b/app/views/elements/edit.html.erb @@ -1,19 +1,17 @@

<%= t("scaffold.pagetitle_edit", model: Element.model_name.human) %>

-<%= turbo_frame_tag dom_id(@element, :frame) do %>

<%= @element.title %> - <%= link_to(tag.i(class: "bi bi-pencil"), @element, class: "btn btn-link text-warning") %>

<%= render "form", element: @element %>
+<% unless modal? %> +
+ <%= link_to t("scaffold.link_show", model: Element.model_name.human), @element %> + <%= link_to t("scaffold.link_index", model: Element.model_name.human(count: 2)), page_elements_path(@element.page) %> +
<% end %> - -
- <%= link_to t("scaffold.link_show", model: Element.model_name.human), @element %> - <%= link_to t("scaffold.link_index", model: Element.model_name.human(count: 2)), page_elements_path(@element.page) %> -
diff --git a/app/views/layouts/_toast.html.slim b/app/views/layouts/_toast.html.slim index 920e3fb..5917715 100644 --- a/app/views/layouts/_toast.html.slim +++ b/app/views/layouts/_toast.html.slim @@ -1,4 +1,4 @@ -.toast class="#{alert ? "text-bg-danger" : ""}" role="alert" aria-live="assertive" aria-atomic="true" data={ controller: "toast" } +.toast class="#{alert ? "text-bg-danger" : "text-bg-info"}" role="alert" aria-live="assertive" aria-atomic="true" data={ controller: "toast" } .toast-header /img src="..." class="rounded me-2" alt="..."> /strong.me-auto = heading diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim index 992a528..ac756a6 100644 --- a/app/views/layouts/application.html.slim +++ b/app/views/layouts/application.html.slim @@ -17,11 +17,10 @@ html data-bs-theme="#{cookies[:"modeTheme"] || "light"}" data-controller="set-th #main-content[data-controller="rich-text-link-targets"] = yield - .toast-container.position-fixed.bottom-0.start-0.p-3 id="toasts" + .toast-container.position-fixed.top-0.end-0.p-3 id="toasts" - if flash.alert = render partial: "layouts/toast", locals: { content: flash.alert, alert: true } - if flash.notice = render partial: "layouts/toast", locals: { content: flash.notice, alert: false } - /footer.container-fluid.mt-auto.border-top - = Rails.configuration.build_version && "Version: #{Rails.configuration.build_version}" + = turbo_frame_tag "modal" diff --git a/app/views/layouts/modal.html.slim b/app/views/layouts/modal.html.slim new file mode 100644 index 0000000..4a3c12e --- /dev/null +++ b/app/views/layouts/modal.html.slim @@ -0,0 +1,9 @@ += turbo_frame_tag "modal" do + dialog.bg-body id="modal" data={ controller: :dialog } + .float-end + form method="dialog" + button.btn.btn-outline-danger + i.bi.bi-x-lg + = yield + +data-action="turbo:submit-end->turbo-modal#submitEnd" diff --git a/app/views/pages/_page.html.erb b/app/views/pages/_page.html.erb index 3505c40..7fa2aa0 100644 --- a/app/views/pages/_page.html.erb +++ b/app/views/pages/_page.html.erb @@ -11,5 +11,5 @@ <% end %>
- <%= link_to("Sprinte zum ersten Element", "##{dom_id(page.elements.first)}", class: "visually-hidden", data: {controller: :hotkey, hotkey: "e", turbo: false}) if page.elements.first %> + <%= link_to("Springe zum ersten Element", "##{dom_id(page.elements.first)}", class: "visually-hidden", data: {controller: :hotkey, hotkey: "e", turbo: false}) if page.elements.first %>
diff --git a/app/views/pages/edit.html.erb b/app/views/pages/edit.html.erb index 6983a18..f809d03 100644 --- a/app/views/pages/edit.html.erb +++ b/app/views/pages/edit.html.erb @@ -2,7 +2,9 @@ <%= render "form", page: @page %> +<% unless modal? %>
<%= link_to t("scaffold.link_show", model: Page.model_name.human), @page %> <%= link_to t("scaffold.link_index", model: Page.model_name.human(count: 2)), report_pages_path(@page.report) %>
+<% end %> diff --git a/app/views/pages/show.html.slim b/app/views/pages/show.html.slim index c2c8f8b..d5045b1 100644 --- a/app/views/pages/show.html.slim +++ b/app/views/pages/show.html.slim @@ -11,7 +11,8 @@ h2 = turbo_frame_tag(dom_id(@page, :notes)) do = render partial: "pages/notes", locals: { page: @page } -.action-row - = link_to t("scaffold.link_edit", model: @page.model_name.human), edit_page_path(@page) - = link_to t("scaffold.link_index", model: @page.model_name.human(count: 2)), report_pages_path(@page.report) - = button_to t("scaffold.link_destroy", model: @page.model_name.human), @page, method: :delete, class: "btn btn-outline-danger" +- unless modal? + .action-row + = link_to t("scaffold.link_edit", model: @page.model_name.human), edit_page_path(@page) + = link_to t("scaffold.link_index", model: @page.model_name.human(count: 2)), report_pages_path(@page.report) + = button_to t("scaffold.link_destroy", model: @page.model_name.human), @page, method: :delete, class: "btn btn-outline-danger" diff --git a/app/views/reports/show.html.slim b/app/views/reports/show.html.slim index 405b87c..3fc7196 100644 --- a/app/views/reports/show.html.slim +++ b/app/views/reports/show.html.slim @@ -1,29 +1,32 @@ +.border-bottom.mb-3 + h1 + i.bi.bi-journal-text.me-2 + = @report.name div - small.float-end - | Erstellt am + small + 'Erstellt am = l(@report.created_at, format: :short) - | , zuletzt bearbeitet am + ', zuletzt bearbeitet am = l(@report.updated_at, format: :short) -h1 - i.bi.bi-journal-text.me-2 - = @report.name -- if @report.comment - .smb-4.lead.mb-3 - = @report.comment +.smb-4.lead.mb-5 + = @report.comment || tag.i("leer") - if @current_page - h2 - i.bi.bi-file-earmark-check - =< @current_page.position - =< @current_page.path - p - 'URL: - = safe_display(@current_page.full_url) { link_to(_1, _1, target: :_blank) } - p.actions - .d-flex.justify-content-end data-controller="details-list" data-target-id="element_list" - .btn-group.me-3 - = link_to("Alle zu [s]", "#", data: { action: "click->details-list#closeAll", controller: :hotkey, hotkey: "s" }, class: "btn btn-outline-secondary") - = link_to("Alle auf [a]", "#", data: { action: "click->details-list#openAll", controller: :hotkey, hotkey: "a" }, class: "btn btn-outline-secondary") - = button_to(tag.i(class: "bi bi-trash"), page_path(@current_page), method: :delete, class: "btn btn-outline-danger", form: {data: { turbo_confirm: "Bist du sicher?" }}) + .current_page data-controller="details-list" data-target-id="element_list" + .border-bottom.mb-3.d-flex + h2 + i.bi.bi-file-earmark-check + =< @current_page.position + =< @current_page.path + = page_menu(@current_page) + p + 'URL: + = safe_display(@current_page.full_url) { link_to(_1, _1, target: :_blank) } + p.actions + .d-flex.justify-content-end + .btn-group.me-3.visually-hidden + = link_to("Alle zu [s]", "#", data: { action: "click->details-list#closeAll", controller: :hotkey, hotkey: "s" }, class: "btn btn-outline-secondary") + = link_to("Alle auf [a]", "#", data: { action: "click->details-list#openAll", controller: :hotkey, hotkey: "a" }, class: "btn btn-outline-secondary") + /= button_to(tag.i(class: "bi bi-trash"), page_path(@current_page), method: :delete, class: "btn btn-outline-danger", form: {data: { turbo_confirm: "Bist du sicher?" }}) .row .col-lg-3.col-md-4.col-sm-12 .page_nav.sticky-top diff --git a/app/views/success_criteria/_body.html.slim b/app/views/success_criteria/_body.html.slim index 45a40d3..e0c4190 100644 --- a/app/views/success_criteria/_body.html.slim +++ b/app/views/success_criteria/_body.html.slim @@ -44,7 +44,7 @@ .text-end.fw-bold = SuccessCriterion.human_attribute_name(:quick_fix) .col-md-8.col-lg-9 = success_criterion.quick_fix - .row.mb-3 + .row .col-md-4.col-lg-3 .text-end.fw-bold = SuccessCriterion.human_attribute_name(:test_instructions) .col-md-8.col-lg-9 diff --git a/app/views/success_criteria/_form.html.slim b/app/views/success_criteria/_form.html.slim index fe12bf5..c67c2b0 100644 --- a/app/views/success_criteria/_form.html.slim +++ b/app/views/success_criteria/_form.html.slim @@ -6,4 +6,5 @@ = form.rich_text_area :quick_fix = form.rich_text_area :test_comment = form.submit class: "btn btn-primary" - = link_to "Abbrechen", success_criterion.persisted? ? success_criterion : success_criterion.element, class: "btn btn-outline-secondary" +- unless modal? + =< link_to "Abbrechen", success_criterion.persisted? ? success_criterion : success_criterion.element, class: "btn btn-outline-secondary" diff --git a/app/views/success_criteria/_header.html.slim b/app/views/success_criteria/_header.html.slim index db31ae4..b7a2fa6 100644 --- a/app/views/success_criteria/_header.html.slim +++ b/app/views/success_criteria/_header.html.slim @@ -19,4 +19,4 @@ summary.d-flex.align-items-start id=dom_id(success_criterion, :header) = success_criterion_badge(success_criterion.check.external_number, extra_classes: "text-bg-info me-1") = success_criterion_badge(success_criterion.level, extra_classes: "sc-level-#{success_criterion.level.to_s.downcase} me-1") - i.bi.bi-grip-vertical.handle + i.bi.bi-grip-vertical.handle.me-1 diff --git a/app/views/success_criteria/destroy.turbo_stream.slim b/app/views/success_criteria/destroy.turbo_stream.slim index 6848f33..f373009 100644 --- a/app/views/success_criteria/destroy.turbo_stream.slim +++ b/app/views/success_criteria/destroy.turbo_stream.slim @@ -1,4 +1,6 @@ = turbo_stream.remove dom_id(@success_criterion) - @success_criterion.element.success_criteria.reject { _1 == @success_criterion }.each do |sc| - Rails.logger.debug "Send to sc #{sc.id}" - = turbo_stream.update dom_id(sc, :position), "#{sc.page.position}.#{sc.element.position}.#{sc.position}" \ No newline at end of file + = turbo_stream.update dom_id(sc, :position), "#{sc.page.position}.#{sc.element.position}.#{sc.position}" + += turbo_stream_toast("Erfolgskriterium wurde gelöscht", true) diff --git a/app/views/success_criteria/edit.html.slim b/app/views/success_criteria/edit.html.slim index d3c144a..e1a2a31 100644 --- a/app/views/success_criteria/edit.html.slim +++ b/app/views/success_criteria/edit.html.slim @@ -1,19 +1,7 @@ -h1 - = t("scaffold.pagetitle_edit", model: SuccessCriterion.model_name.human) -= turbo_frame_tag(dom_id(@success_criterion, :frame)) do - .d-flex - h2.my-2 Bearbeiten - .ms-auto - = success_criterion_menu(@success_criterion, false) - /= dropdown_menu(klass: "mt-3 b-0") do - ul.list-group - li.list-group-item - = success_criterion_edit_button(@success_criterion, true) - li.list-group-item - = button_to(tag.i(class: "bi bi-trash") + " Löschen".html_safe, @success_criterion, method: :delete, class: "btn text-danger", data: { turbo_confirm: "Bist du sicher?"}) - - .mb-3 - = render "form", success_criterion: @success_criterion -.action-row - = link_to t("scaffold.link_show", model: SuccessCriterion.model_name.human), @success_criterion - = link_to t("scaffold.link_index", model: SuccessCriterion.model_name.human(count: 2)), element_success_criteria_path(@success_criterion.element) +h2 Erfolgskriterium bearbeiten +.mb-3 + = render "form", success_criterion: @success_criterion +- unless modal? + .action-row + = link_to t("scaffold.link_show", model: SuccessCriterion.model_name.human), @success_criterion + = link_to t("scaffold.link_index", model: SuccessCriterion.model_name.human(count: 2)), element_success_criteria_path(@success_criterion.element) diff --git a/app/views/success_criteria/update.turbo_stream.slim b/app/views/success_criteria/update.turbo_stream.slim index dc9c79e..c6e53ff 100644 --- a/app/views/success_criteria/update.turbo_stream.slim +++ b/app/views/success_criteria/update.turbo_stream.slim @@ -3,5 +3,4 @@ - @success_criterion.element.success_criteria.each do |sc| = turbo_stream.update(dom_id(sc, :position), sc.number) - -= turbo_stream_toast("Erfolgskriterium gespeichert: #{t("activerecord.attributes.success_criterion.results/#{@success_criterion.result}")}", false) \ No newline at end of file += turbo_stream_toast("Erfolgskriterium gespeichert: #{t("activerecord.attributes.success_criterion.results/#{@success_criterion.result}")}", false) diff --git a/package-lock.json b/package-lock.json index a189879..00e45c0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,10 +6,13 @@ "": { "name": "app", "dependencies": { + "@github/hotkey": "^3.1.1", "@hotwired/stimulus": "^3.2.2", "@hotwired/turbo-rails": "^8.0.4", "@popperjs/core": "^2.11.8", "@rails/actiontext": "^7.1.3-4", + "@rails/request.js": "^0.0.11", + "@stimulus-components/lightbox": "^4.0.0", "autoprefixer": "^10.4.19", "bootstrap": "^5.3.3", "bootstrap-icons": "^1.11.3", @@ -18,10 +21,13 @@ "postcss": "^8.4.39", "postcss-cli": "^11.0.0", "sass": "^1.77.8", + "sortablejs": "^1.15.3", "trix": "^2.1.3" }, "devDependencies": { - "tabby-agent": "^1.7.0" + "tabby-agent": "^1.7.0", + "vscode-css-languageserver-bin": "^1.4.0", + "vscode-langservers-extracted": "^4.10.0" } }, "node_modules/@esbuild/linux-x64": { @@ -40,6 +46,11 @@ "node": ">=18" } }, + "node_modules/@github/hotkey": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@github/hotkey/-/hotkey-3.1.1.tgz", + "integrity": "sha512-H30I6XDO3gFSgLuEuHoMBRZG9c3uCKNdAcYklL1FaZDPdU1bXfgjnpzGDPcUr0U6eGQ+T3XLY9slatwZYWL1dA==" + }, "node_modules/@hotwired/stimulus": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/@hotwired/stimulus/-/stimulus-3.2.2.tgz", @@ -137,6 +148,11 @@ "spark-md5": "^3.0.1" } }, + "node_modules/@rails/request.js": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@rails/request.js/-/request.js-0.0.11.tgz", + "integrity": "sha512-2U3uYS0kbljt+pAstN+LIlZOl7xmOKig5N6FrvtUWO1wq0zR1Hf90fHfD2SYiyV8yH1nyKpoTmbLqWT0xe1zDg==" + }, "node_modules/@sindresorhus/merge-streams": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", @@ -149,6 +165,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@stimulus-components/lightbox": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@stimulus-components/lightbox/-/lightbox-4.0.0.tgz", + "integrity": "sha512-pj4PfnGANbc3Ef6I/5aPeRS8Tj2rK1b9dn8rlpJZs1K9narqM57ZtUTutHZsnOv95+4LqQVktj+auMgz+LKzjw==", + "dependencies": { + "lightgallery": "^2.7.2" + }, + "peerDependencies": { + "@hotwired/stimulus": "^3.0.0" + } + }, + "node_modules/@vscode/l10n": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", + "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==", + "dev": true + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -241,6 +274,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, "node_modules/bootstrap": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", @@ -412,6 +451,45 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "license": "MIT" }, + "node_modules/core-js": { + "version": "3.39.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", + "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", + "dev": true, + "hasInstallScript": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/css-select": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", + "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", + "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/debug": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", @@ -438,6 +516,61 @@ "node": ">= 0.6.0" } }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", + "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, "node_modules/electron-to-chromium": { "version": "1.4.827", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.827.tgz", @@ -450,6 +583,18 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/esbuild": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", @@ -630,6 +775,15 @@ "node": ">=4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -702,6 +856,12 @@ "node": ">=0.12.0" } }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true + }, "node_modules/jsonfile": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", @@ -714,6 +874,14 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/lightgallery": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/lightgallery/-/lightgallery-2.8.1.tgz", + "integrity": "sha512-K9bRsrKQM4UyjNUXOX+mW1IMWKvWbDmK42x5nNzgxsi5CNEYMfLoRsA27F1lXp+yegeqdXusymS9w16Fd8a3Rg==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/lilconfig": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", @@ -784,6 +952,16 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/node-html-parser": { + "version": "6.1.13", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.13.tgz", + "integrity": "sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==", + "dev": true, + "dependencies": { + "css-select": "^5.1.0", + "he": "1.2.0" + } + }, "node_modules/node-releases": { "version": "2.0.14", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", @@ -836,6 +1014,18 @@ "node": ">=0.10.0" } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/path-type": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", @@ -1059,6 +1249,18 @@ "node": ">=8.10.0" } }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, + "node_modules/request-light": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.7.0.tgz", + "integrity": "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==", + "dev": true + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -1154,6 +1356,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/sortablejs": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.3.tgz", + "integrity": "sha512-zdK3/kwwAK1cJgy1rwl1YtNTbRmc8qW/+vgXf75A7NHag5of4pyI6uK86ktmQETyWRH7IGaE73uZOOBcGxgqZg==" + }, "node_modules/source-map-js": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", @@ -1252,6 +1459,19 @@ "integrity": "sha512-LqMp67LiKMQytAHKqNL1Jgmfz69ViW+WBOQTPA2BlMIuxic1mw5vHgDtOE0bvvojUdjAxh0EJtLpJn6BC/2JKw==", "license": "MIT" }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/undefsafe": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", @@ -1309,6 +1529,239 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/vscode-css-languageserver-bin": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/vscode-css-languageserver-bin/-/vscode-css-languageserver-bin-1.4.0.tgz", + "integrity": "sha512-KWrF5f4RYYe8RBDfqb1c0Sdf9xPS2Ly/Z/T18H+uUOMw2QyzIrkxv4bMKy5GFfPm4479k6Ln4ji4UHqSmhGf3g==", + "dev": true, + "dependencies": { + "vscode-css-languageservice": "^3.0.9-next.18", + "vscode-languageserver": "^4.1.3", + "vscode-languageserver-protocol-foldingprovider": "^2.0.1" + }, + "bin": { + "css-languageserver": "cssServerMain.js" + }, + "engines": { + "node": "*" + } + }, + "node_modules/vscode-css-languageservice": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13.tgz", + "integrity": "sha512-RWkO/c/A7iXhHEy3OuEqkCqavDjpD4NF2Ca8vjai+ZtEYNeHrm1ybTnBYLP4Ft1uXvvaaVtYA9HrDjD6+CUONg==", + "dev": true, + "dependencies": { + "vscode-languageserver-types": "^3.13.0", + "vscode-nls": "^4.0.0" + } + }, + "node_modules/vscode-html-languageservice": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.3.1.tgz", + "integrity": "sha512-ysUh4hFeW/WOWz/TO9gm08xigiSsV/FOAZ+DolgJfeLftna54YdmZ4A+lIn46RbdO3/Qv5QHTn1ZGqmrXQhZyA==", + "dev": true, + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "^3.17.5", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/vscode-json-languageservice": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.4.1.tgz", + "integrity": "sha512-5czFGNyVPxz3ZJYl8R3a3SuIj5gjhmGF4Wv05MRPvD4DEnHK6b8km4VbNMJNHBlTCh7A0aHzUbPVzo+0C18mCA==", + "dev": true, + "dependencies": { + "@vscode/l10n": "^0.0.18", + "jsonc-parser": "^3.3.1", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "^3.17.5", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/vscode-jsonrpc": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-langservers-extracted": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/vscode-langservers-extracted/-/vscode-langservers-extracted-4.10.0.tgz", + "integrity": "sha512-EFf9uQI4dAKbzMQFjDvVm1xJq1DXAQvBEuEfPGrK/xzfsL5xWTfIuRr90NgfmqwO+IEt6vLZm9EOj6R66xIifg==", + "dev": true, + "dependencies": { + "@vscode/l10n": "^0.0.18", + "core-js": "^3.20.1", + "jsonc-parser": "^3.2.1", + "regenerator-runtime": "^0.13.9", + "request-light": "^0.7.0", + "semver": "^7.6.1", + "typescript": "^4.0.5", + "vscode-css-languageservice": "^6.2.14", + "vscode-html-languageservice": "^5.2.0", + "vscode-json-languageservice": "^5.3.11", + "vscode-languageserver": "^10.0.0-next.3", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-languageserver-types": "^3.17.5", + "vscode-markdown-languageservice": "^0.5.0-alpha.6", + "vscode-nls": "^5.2.0", + "vscode-uri": "^3.0.8" + }, + "bin": { + "vscode-css-language-server": "bin/vscode-css-language-server", + "vscode-eslint-language-server": "bin/vscode-eslint-language-server", + "vscode-html-language-server": "bin/vscode-html-language-server", + "vscode-json-language-server": "bin/vscode-json-language-server", + "vscode-markdown-language-server": "bin/vscode-markdown-language-server" + } + }, + "node_modules/vscode-langservers-extracted/node_modules/vscode-css-languageservice": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.1.tgz", + "integrity": "sha512-1BzTBuJfwMc3A0uX4JBdJgoxp74cjj4q2mDJdp49yD/GuAq4X0k5WtK6fNcMYr+FfJ9nqgR6lpfCSZDkARJ5qQ==", + "dev": true, + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "3.17.5", + "vscode-uri": "^3.0.8" + } + }, + "node_modules/vscode-langservers-extracted/node_modules/vscode-jsonrpc": { + "version": "9.0.0-next.6", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.6.tgz", + "integrity": "sha512-KCSvUNsFiVciG9iqjJKBZOd66CN3ZKohDlYRmoOi+pd8l15MFLZ8wRG4c+wuzePGba/8WcCG2TM+C/GVlvuaeA==", + "dev": true, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-langservers-extracted/node_modules/vscode-languageserver": { + "version": "10.0.0-next.11", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.11.tgz", + "integrity": "sha512-cmobSrVDYhlh/t02vz/bV8nNpds8mus5HnILULae2iAvOjoaJPnTAp0jJWoYdUqTpIVzT9JV6JMKqLEvdqpeqg==", + "dev": true, + "dependencies": { + "vscode-languageserver-protocol": "3.17.6-next.11" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-langservers-extracted/node_modules/vscode-languageserver-protocol": { + "version": "3.17.6-next.11", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.11.tgz", + "integrity": "sha512-GeJxEp1TiLsp79f8WG5n10wLViXfgFKb99hU9K8m7KDWM95/QFEqWkm79f9LVm54tUK74I91a9EeiQLCS/FABQ==", + "dev": true, + "dependencies": { + "vscode-jsonrpc": "9.0.0-next.6", + "vscode-languageserver-types": "3.17.6-next.5" + } + }, + "node_modules/vscode-langservers-extracted/node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { + "version": "3.17.6-next.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.5.tgz", + "integrity": "sha512-QFmf3Yl1tCgUQfA77N9Me/LXldJXkIVypQbty2rJ1DNHQkC+iwvm4Z2tXg9czSwlhvv0pD4pbF5mT7WhAglolw==", + "dev": true + }, + "node_modules/vscode-langservers-extracted/node_modules/vscode-nls": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", + "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==", + "dev": true + }, + "node_modules/vscode-languageserver": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-4.4.2.tgz", + "integrity": "sha512-61y8Raevi9EigDgg9NelvT9cUAohiEbUl1LOwQQgOCAaNX62yKny/ddi0uC+FUTm4CzsjhBu+06R+vYgfCYReA==", + "dev": true, + "dependencies": { + "vscode-languageserver-protocol": "^3.10.3", + "vscode-uri": "^1.0.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "dev": true, + "dependencies": { + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" + } + }, + "node_modules/vscode-languageserver-protocol-foldingprovider": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol-foldingprovider/-/vscode-languageserver-protocol-foldingprovider-2.0.1.tgz", + "integrity": "sha512-N8bOS8i0xuQMn/y0bijyefDbOsMl6hiH6LDREYWavTLTM5jbj44EiQfStsbmAv/0eaFKkL/jf5hW7nWwBy2HBw==", + "dev": true, + "dependencies": { + "vscode-languageserver-protocol": "^3.7.2", + "vscode-languageserver-types": "^3.7.2" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "dev": true + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true + }, + "node_modules/vscode-languageserver/node_modules/vscode-uri": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz", + "integrity": "sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ==", + "dev": true + }, + "node_modules/vscode-markdown-languageservice": { + "version": "0.5.0-alpha.8", + "resolved": "https://registry.npmjs.org/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.5.0-alpha.8.tgz", + "integrity": "sha512-b2NgVMZvzI/7hRL32Kcu9neAAPFQzkcf/Fqwlxbz9p1/Q7aIorGACOGGo00s72AJtwjkCJ29eVJwUlFMFbPKqA==", + "dev": true, + "dependencies": { + "@vscode/l10n": "^0.0.10", + "node-html-parser": "^6.1.5", + "picomatch": "^2.3.1", + "vscode-languageserver-protocol": "^3.17.1", + "vscode-languageserver-textdocument": "^1.0.11", + "vscode-uri": "^3.0.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/vscode-markdown-languageservice/node_modules/@vscode/l10n": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.10.tgz", + "integrity": "sha512-E1OCmDcDWa0Ya7vtSjp/XfHFGqYJfh+YPC1RkATU71fTac+j1JjCcB3qwSzmlKAighx2WxhLlfhS0RwAN++PFQ==", + "dev": true + }, + "node_modules/vscode-nls": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz", + "integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==", + "dev": true + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "dev": true + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", diff --git a/package.json b/package.json index b8c7a06..38a1f98 100644 --- a/package.json +++ b/package.json @@ -31,6 +31,8 @@ "defaults" ], "devDependencies": { - "tabby-agent": "^1.7.0" + "tabby-agent": "^1.7.0", + "vscode-css-languageserver-bin": "^1.4.0", + "vscode-langservers-extracted": "^4.10.0" } } diff --git a/yarn.lock b/yarn.lock index b1abead..e4005fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -124,7 +124,7 @@ "@github/hotkey@^3.1.1": version "3.1.1" - resolved "https://registry.yarnpkg.com/@github/hotkey/-/hotkey-3.1.1.tgz#61fdf9993378b402bd9cdd494ee8149fe3172b0c" + resolved "https://registry.npmjs.org/@github/hotkey/-/hotkey-3.1.1.tgz" integrity sha512-H30I6XDO3gFSgLuEuHoMBRZG9c3uCKNdAcYklL1FaZDPdU1bXfgjnpzGDPcUr0U6eGQ+T3XLY9slatwZYWL1dA== "@hotwired/stimulus@^3.2.2": @@ -192,7 +192,7 @@ "@rails/request.js@^0.0.11": version "0.0.11" - resolved "https://registry.yarnpkg.com/@rails/request.js/-/request.js-0.0.11.tgz#4d9be25a49d97911c64ccd0f00b79d57fca4c3b4" + resolved "https://registry.npmjs.org/@rails/request.js/-/request.js-0.0.11.tgz" integrity sha512-2U3uYS0kbljt+pAstN+LIlZOl7xmOKig5N6FrvtUWO1wq0zR1Hf90fHfD2SYiyV8yH1nyKpoTmbLqWT0xe1zDg== "@sindresorhus/merge-streams@^2.1.0": @@ -202,11 +202,21 @@ "@stimulus-components/lightbox@^4.0.0": version "4.0.0" - resolved "https://registry.yarnpkg.com/@stimulus-components/lightbox/-/lightbox-4.0.0.tgz#dbe56146a3d14582a13e568406292d52aa56cbb0" + resolved "https://registry.npmjs.org/@stimulus-components/lightbox/-/lightbox-4.0.0.tgz" integrity sha512-pj4PfnGANbc3Ef6I/5aPeRS8Tj2rK1b9dn8rlpJZs1K9narqM57ZtUTutHZsnOv95+4LqQVktj+auMgz+LKzjw== dependencies: lightgallery "^2.7.2" +"@vscode/l10n@^0.0.10": + version "0.0.10" + resolved "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.10.tgz" + integrity sha512-E1OCmDcDWa0Ya7vtSjp/XfHFGqYJfh+YPC1RkATU71fTac+j1JjCcB3qwSzmlKAighx2WxhLlfhS0RwAN++PFQ== + +"@vscode/l10n@^0.0.18": + version "0.0.18" + resolved "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz" + integrity sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ== + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" @@ -249,6 +259,11 @@ binary-extensions@^2.0.0: resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + bootstrap-icons@^1.11.3: version "1.11.3" resolved "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz" @@ -330,6 +345,27 @@ concat-map@0.0.1: resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +core-js@^3.20.1: + version "3.39.0" + resolved "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz" + integrity sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g== + +css-select@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz" + integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + +css-what@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + debug@^4: version "4.3.5" resolved "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz" @@ -342,6 +378,36 @@ dependency-graph@^0.11.0: resolved "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz" integrity sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg== +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^3.0.1: + version "3.1.0" + resolved "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz" + integrity sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + electron-to-chromium@^1.4.820: version "1.4.827" resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.827.tgz" @@ -352,6 +418,11 @@ emoji-regex@^8.0.0: resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +entities@^4.2.0: + version "4.5.0" + resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + esbuild@^0.23.0: version "0.23.0" resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz" @@ -470,6 +541,11 @@ has-flag@^3.0.0: resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== +he@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + ignore-by-default@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz" @@ -514,6 +590,11 @@ is-number@^7.0.0: resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== +jsonc-parser@^3.2.1, jsonc-parser@^3.3.1: + version "3.3.1" + resolved "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz" + integrity sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ== + jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" @@ -524,9 +605,9 @@ jsonfile@^6.0.1: graceful-fs "^4.1.6" lightgallery@^2.7.2: - version "2.7.2" - resolved "https://registry.yarnpkg.com/lightgallery/-/lightgallery-2.7.2.tgz#9309216986fbce60602b851e1ef80cf40769c3d3" - integrity sha512-Ewdcg9UPDqV0HGZeD7wNE4uYejwH2u0fMo5VAr6GHzlPYlhItJvjhLTR0cL0V1HjhMsH39PAom9iv69ewitLWw== + version "2.8.1" + resolved "https://registry.npmjs.org/lightgallery/-/lightgallery-2.8.1.tgz" + integrity sha512-K9bRsrKQM4UyjNUXOX+mW1IMWKvWbDmK42x5nNzgxsi5CNEYMfLoRsA27F1lXp+yegeqdXusymS9w16Fd8a3Rg== lilconfig@^3.1.1: version "3.1.2" @@ -563,6 +644,14 @@ nanoid@^3.3.7: resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz" integrity sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g== +node-html-parser@^6.1.5: + version "6.1.13" + resolved "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.13.tgz" + integrity sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg== + dependencies: + css-select "^5.1.0" + he "1.2.0" + node-releases@^2.0.14: version "2.0.14" resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz" @@ -594,6 +683,13 @@ normalize-range@^0.1.2: resolved "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz" integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + path-type@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz" @@ -691,6 +787,16 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +regenerator-runtime@^0.13.9: + version "0.13.11" + resolved "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + +request-light@^0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/request-light/-/request-light-0.7.0.tgz" + integrity sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q== + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" @@ -717,7 +823,7 @@ sass@^1.77.8: immutable "^4.0.0" source-map-js ">=0.6.2 <2.0.0" -semver@^7.5.3: +semver@^7.5.3, semver@^7.6.1: version "7.6.2" resolved "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz" integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== @@ -736,7 +842,7 @@ slash@^5.0.0, slash@^5.1.0: sortablejs@^1.15.3: version "1.15.3" - resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.15.3.tgz#033668db5ebfb11167d1249ab88e748f27959e29" + resolved "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.3.tgz" integrity sha512-zdK3/kwwAK1cJgy1rwl1YtNTbRmc8qW/+vgXf75A7NHag5of4pyI6uK86ktmQETyWRH7IGaE73uZOOBcGxgqZg== "source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.0: @@ -799,6 +905,11 @@ trix@^2.1.3: resolved "https://registry.npmjs.org/trix/-/trix-2.1.3.tgz" integrity sha512-LqMp67LiKMQytAHKqNL1Jgmfz69ViW+WBOQTPA2BlMIuxic1mw5vHgDtOE0bvvojUdjAxh0EJtLpJn6BC/2JKw== +typescript@^4.0.5: + version "4.9.5" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + undefsafe@^2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz" @@ -822,6 +933,172 @@ update-browserslist-db@^1.1.0: escalade "^3.1.2" picocolors "^1.0.1" +vscode-css-languageserver-bin@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/vscode-css-languageserver-bin/-/vscode-css-languageserver-bin-1.4.0.tgz" + integrity sha512-KWrF5f4RYYe8RBDfqb1c0Sdf9xPS2Ly/Z/T18H+uUOMw2QyzIrkxv4bMKy5GFfPm4479k6Ln4ji4UHqSmhGf3g== + dependencies: + vscode-css-languageservice "^3.0.9-next.18" + vscode-languageserver "^4.1.3" + vscode-languageserver-protocol-foldingprovider "^2.0.1" + +vscode-css-languageservice@^3.0.9-next.18: + version "3.0.13" + resolved "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13.tgz" + integrity sha512-RWkO/c/A7iXhHEy3OuEqkCqavDjpD4NF2Ca8vjai+ZtEYNeHrm1ybTnBYLP4Ft1uXvvaaVtYA9HrDjD6+CUONg== + dependencies: + vscode-languageserver-types "^3.13.0" + vscode-nls "^4.0.0" + +vscode-css-languageservice@^6.2.14: + version "6.3.1" + resolved "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.1.tgz" + integrity sha512-1BzTBuJfwMc3A0uX4JBdJgoxp74cjj4q2mDJdp49yD/GuAq4X0k5WtK6fNcMYr+FfJ9nqgR6lpfCSZDkARJ5qQ== + dependencies: + "@vscode/l10n" "^0.0.18" + vscode-languageserver-textdocument "^1.0.12" + vscode-languageserver-types "3.17.5" + vscode-uri "^3.0.8" + +vscode-html-languageservice@^5.2.0: + version "5.3.1" + resolved "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.3.1.tgz" + integrity sha512-ysUh4hFeW/WOWz/TO9gm08xigiSsV/FOAZ+DolgJfeLftna54YdmZ4A+lIn46RbdO3/Qv5QHTn1ZGqmrXQhZyA== + dependencies: + "@vscode/l10n" "^0.0.18" + vscode-languageserver-textdocument "^1.0.12" + vscode-languageserver-types "^3.17.5" + vscode-uri "^3.0.8" + +vscode-json-languageservice@^5.3.11: + version "5.4.1" + resolved "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.4.1.tgz" + integrity sha512-5czFGNyVPxz3ZJYl8R3a3SuIj5gjhmGF4Wv05MRPvD4DEnHK6b8km4VbNMJNHBlTCh7A0aHzUbPVzo+0C18mCA== + dependencies: + "@vscode/l10n" "^0.0.18" + jsonc-parser "^3.3.1" + vscode-languageserver-textdocument "^1.0.12" + vscode-languageserver-types "^3.17.5" + vscode-uri "^3.0.8" + +vscode-jsonrpc@8.2.0: + version "8.2.0" + resolved "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz" + integrity sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA== + +vscode-jsonrpc@9.0.0-next.6: + version "9.0.0-next.6" + resolved "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.6.tgz" + integrity sha512-KCSvUNsFiVciG9iqjJKBZOd66CN3ZKohDlYRmoOi+pd8l15MFLZ8wRG4c+wuzePGba/8WcCG2TM+C/GVlvuaeA== + +vscode-langservers-extracted@^4.10.0: + version "4.10.0" + resolved "https://registry.npmjs.org/vscode-langservers-extracted/-/vscode-langservers-extracted-4.10.0.tgz" + integrity sha512-EFf9uQI4dAKbzMQFjDvVm1xJq1DXAQvBEuEfPGrK/xzfsL5xWTfIuRr90NgfmqwO+IEt6vLZm9EOj6R66xIifg== + dependencies: + "@vscode/l10n" "^0.0.18" + core-js "^3.20.1" + jsonc-parser "^3.2.1" + regenerator-runtime "^0.13.9" + request-light "^0.7.0" + semver "^7.6.1" + typescript "^4.0.5" + vscode-css-languageservice "^6.2.14" + vscode-html-languageservice "^5.2.0" + vscode-json-languageservice "^5.3.11" + vscode-languageserver "^10.0.0-next.3" + vscode-languageserver-textdocument "^1.0.11" + vscode-languageserver-types "^3.17.5" + vscode-markdown-languageservice "^0.5.0-alpha.6" + vscode-nls "^5.2.0" + vscode-uri "^3.0.8" + +vscode-languageserver-protocol-foldingprovider@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/vscode-languageserver-protocol-foldingprovider/-/vscode-languageserver-protocol-foldingprovider-2.0.1.tgz" + integrity sha512-N8bOS8i0xuQMn/y0bijyefDbOsMl6hiH6LDREYWavTLTM5jbj44EiQfStsbmAv/0eaFKkL/jf5hW7nWwBy2HBw== + dependencies: + vscode-languageserver-protocol "^3.7.2" + vscode-languageserver-types "^3.7.2" + +vscode-languageserver-protocol@3.17.6-next.11: + version "3.17.6-next.11" + resolved "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.11.tgz" + integrity sha512-GeJxEp1TiLsp79f8WG5n10wLViXfgFKb99hU9K8m7KDWM95/QFEqWkm79f9LVm54tUK74I91a9EeiQLCS/FABQ== + dependencies: + vscode-jsonrpc "9.0.0-next.6" + vscode-languageserver-types "3.17.6-next.5" + +vscode-languageserver-protocol@^3.10.3, vscode-languageserver-protocol@^3.17.1, vscode-languageserver-protocol@^3.7.2: + version "3.17.5" + resolved "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz" + integrity sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg== + dependencies: + vscode-jsonrpc "8.2.0" + vscode-languageserver-types "3.17.5" + +vscode-languageserver-textdocument@^1.0.11, vscode-languageserver-textdocument@^1.0.12: + version "1.0.12" + resolved "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz" + integrity sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA== + +vscode-languageserver-types@3.17.5, vscode-languageserver-types@^3.13.0, vscode-languageserver-types@^3.17.5, vscode-languageserver-types@^3.7.2: + version "3.17.5" + resolved "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz" + integrity sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg== + +vscode-languageserver-types@3.17.6-next.5: + version "3.17.6-next.5" + resolved "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.5.tgz" + integrity sha512-QFmf3Yl1tCgUQfA77N9Me/LXldJXkIVypQbty2rJ1DNHQkC+iwvm4Z2tXg9czSwlhvv0pD4pbF5mT7WhAglolw== + +vscode-languageserver@^10.0.0-next.3: + version "10.0.0-next.11" + resolved "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.11.tgz" + integrity sha512-cmobSrVDYhlh/t02vz/bV8nNpds8mus5HnILULae2iAvOjoaJPnTAp0jJWoYdUqTpIVzT9JV6JMKqLEvdqpeqg== + dependencies: + vscode-languageserver-protocol "3.17.6-next.11" + +vscode-languageserver@^4.1.3: + version "4.4.2" + resolved "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-4.4.2.tgz" + integrity sha512-61y8Raevi9EigDgg9NelvT9cUAohiEbUl1LOwQQgOCAaNX62yKny/ddi0uC+FUTm4CzsjhBu+06R+vYgfCYReA== + dependencies: + vscode-languageserver-protocol "^3.10.3" + vscode-uri "^1.0.5" + +vscode-markdown-languageservice@^0.5.0-alpha.6: + version "0.5.0-alpha.8" + resolved "https://registry.npmjs.org/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.5.0-alpha.8.tgz" + integrity sha512-b2NgVMZvzI/7hRL32Kcu9neAAPFQzkcf/Fqwlxbz9p1/Q7aIorGACOGGo00s72AJtwjkCJ29eVJwUlFMFbPKqA== + dependencies: + "@vscode/l10n" "^0.0.10" + node-html-parser "^6.1.5" + picomatch "^2.3.1" + vscode-languageserver-protocol "^3.17.1" + vscode-languageserver-textdocument "^1.0.11" + vscode-uri "^3.0.7" + +vscode-nls@^4.0.0: + version "4.1.2" + resolved "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz" + integrity sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw== + +vscode-nls@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz" + integrity sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng== + +vscode-uri@^1.0.5: + version "1.0.8" + resolved "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz" + integrity sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ== + +vscode-uri@^3.0.7, vscode-uri@^3.0.8: + version "3.0.8" + resolved "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz" + integrity sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw== + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" From 110d75f2b788d1b78c64a32bf56e90a66274a4bf Mon Sep 17 00:00:00 2001 From: david Date: Sat, 23 Nov 2024 19:24:43 +0100 Subject: [PATCH 24/46] fix lightbox --- app/assets/stylesheets/application.bootstrap.scss | 3 ++- app/javascript/controllers/index.js | 3 +++ yarn.lock | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/application.bootstrap.scss b/app/assets/stylesheets/application.bootstrap.scss index 976c954..1846b2d 100644 --- a/app/assets/stylesheets/application.bootstrap.scss +++ b/app/assets/stylesheets/application.bootstrap.scss @@ -50,6 +50,7 @@ $bootstrap-icons-font-dir: ""; $lg-path-fonts: ""; @import "lightgallery/scss/lightgallery"; + /* * Provides a drop-in pointer for the default Trix stylesheet that will format the toolbar and * the trix-editor content (whether displayed or under editing). Feel free to incorporate this @@ -163,4 +164,4 @@ trix-toolbar .trix-dialog--link { } */ @import "./layout"; -@import "./dropdown"; +@import "./dropdown"; \ No newline at end of file diff --git a/app/javascript/controllers/index.js b/app/javascript/controllers/index.js index 6ad9f94..c4c4879 100644 --- a/app/javascript/controllers/index.js +++ b/app/javascript/controllers/index.js @@ -4,6 +4,9 @@ import { application } from "./application" +import Lightbox from '@stimulus-components/lightbox' +application.register('lightbox', Lightbox) + import AutosubmitController from "./autosubmit_controller" application.register("autosubmit", AutosubmitController) diff --git a/yarn.lock b/yarn.lock index e4005fb..135b0bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -202,7 +202,7 @@ "@stimulus-components/lightbox@^4.0.0": version "4.0.0" - resolved "https://registry.npmjs.org/@stimulus-components/lightbox/-/lightbox-4.0.0.tgz" + resolved "https://registry.yarnpkg.com/@stimulus-components/lightbox/-/lightbox-4.0.0.tgz#dbe56146a3d14582a13e568406292d52aa56cbb0" integrity sha512-pj4PfnGANbc3Ef6I/5aPeRS8Tj2rK1b9dn8rlpJZs1K9narqM57ZtUTutHZsnOv95+4LqQVktj+auMgz+LKzjw== dependencies: lightgallery "^2.7.2" From c36230b8ba13a4fa37c62cb9ef3a4a3d5b2d473f Mon Sep 17 00:00:00 2001 From: david Date: Sat, 23 Nov 2024 21:11:01 +0100 Subject: [PATCH 25/46] edit comment and pdf export --- .../success_criteria_controller.rb | 6 +++- app/models/element.rb | 2 ++ app/models/pdf_documents/base.rb | 31 ++++++++++++---- app/models/pdf_documents/customer_report.rb | 35 +++++++++++++++---- app/models/report.rb | 13 +++++++ app/models/success_criterion.rb | 2 ++ app/views/reports/show.html.slim | 6 ++-- app/views/success_criteria/_body.html.slim | 5 ++- .../success_criteria/edit_comment.html.slim | 5 +++ config/routes.rb | 4 +++ 10 files changed, 90 insertions(+), 19 deletions(-) create mode 100644 app/views/success_criteria/edit_comment.html.slim diff --git a/app/controllers/success_criteria_controller.rb b/app/controllers/success_criteria_controller.rb index f77a931..a180f99 100644 --- a/app/controllers/success_criteria_controller.rb +++ b/app/controllers/success_criteria_controller.rb @@ -2,7 +2,7 @@ class SuccessCriteriaController < ApplicationController before_action :set_element, only: %i[new create index new_from_checklist create_from_checklist] - before_action :set_success_criterion, only: %i[show edit update destroy] + before_action :set_success_criterion, only: %i[show edit update destroy edit_comment] # GET /success_criteria def index @@ -108,6 +108,10 @@ class SuccessCriteriaController < ApplicationController end end + def edit_comment + render_modal() + end + private # Use callbacks to share common setup or constraints between actions. diff --git a/app/models/element.rb b/app/models/element.rb index e0b7ddf..84af4be 100644 --- a/app/models/element.rb +++ b/app/models/element.rb @@ -15,6 +15,8 @@ class Element < ApplicationRecord attachable.variant :thumbnail, resize_to_limit: [ 200, 200 ] end + scope :failed, -> { where(SuccessCriterion.where(result: SuccessCriterion.results[:failed]).arel.exists) } + # Calculate actual conformity level: # - if a success_criterion has result :failed -> the confirmity_level # of that success_criterion is not reached. diff --git a/app/models/pdf_documents/base.rb b/app/models/pdf_documents/base.rb index c21d790..c34aa80 100644 --- a/app/models/pdf_documents/base.rb +++ b/app/models/pdf_documents/base.rb @@ -2,7 +2,7 @@ module PdfDocuments class Base - attr_reader :params + attr_reader :params, :font def initialize(prawn_document, **params) @prawn_document = prawn_document @@ -13,7 +13,8 @@ module PdfDocuments # exta_bold: 'vendor/assets/fonts/Lexend-ExtraBold.ttf', # italic: 'vendor/assets/fonts/Lexend-Regular.ttf' # }) - @prawn_document.font "Helvetica", size: 12 + @font = "Helvetica" + @prawn_document.font @font, size: 12 @params = OpenStruct.new(params) end @@ -40,6 +41,10 @@ module PdfDocuments @prawn_document.markup "

#{text}

" end + def heading4(text) + @prawn_document.markup "

#{text}

" + end + def text(text) @prawn_document.text text, markup_options[:text] end @@ -56,10 +61,10 @@ module PdfDocuments { text: { size: 12, margin_bottom: 5 }, heading1: { style: :bold, size: 26, margin_bottom: 10, margin_top: 0 }, - heading2: { style: :bold, size: 17, margin_bottom: 10, margin_top: 5 }, - heading3: { style: :bold, size: 13, margin_bottom: 10, margin_top: 5 }, - heading4: { style: :bold, size: 12, margin_bottom: 10, margin_top: 5 }, - heading5: { style: :bold, size: 12, margin_bottom: 10, margin_top: 5 }, + heading2: { style: :bold, size: 17, margin_bottom: 10, margin_top: 15 }, + heading3: { style: :bold, size: 13, margin_bottom: 10, margin_top: 15 }, + heading4: { style: :bold, size: 12, margin_bottom: 10, margin_top: 10 }, + heading5: { style: :bold, size: 12, margin_bottom: 10, margin_top: 10 }, heading6: { style: :thin, size: 12, margin_bottom: 10, margin_top: 5 } } end @@ -70,7 +75,7 @@ module PdfDocuments end def prepare_rich_text(rich_text) - { h1: "h4" }.each do |tag, replacement| + { h1: "h5" }.each do |tag, replacement| rich_text = rich_text.to_s.gsub("<#{tag}", "<#{replacement}") rich_text = rich_text.to_s.gsub("", "") end @@ -89,5 +94,17 @@ module PdfDocuments def move_down(...) @prawn_document.move_down(...) end + + def safe_display(value, &block) + return if value.blank? + + yield + end + + def bold(text) + @prawn_document.font(@font, style: :bold) do + @prawn_document.text text + end + end end end diff --git a/app/models/pdf_documents/customer_report.rb b/app/models/pdf_documents/customer_report.rb index 463be57..694642a 100644 --- a/app/models/pdf_documents/customer_report.rb +++ b/app/models/pdf_documents/customer_report.rb @@ -13,18 +13,39 @@ module PdfDocuments heading1 params.report.name rich_text params.report.comment - params.report.elements.each.with_index(1) do |element, element_index| + params.report.export[:elements].each.with_index(1) do |(element, success_criteria), element_index| heading2 "#{element_index} #{element.title}" - formatted_text [ { text: element.path, styles: %i[bold italic underline] } ] move_down(5) - rich_text element.description_html + bold("Pfad: #{element.page.path}") + move_down(5) + rich_text element.description - element.success_criteria.each.with_index(1) do |success_criterion, sc_index| - heading3 "#{element_index}.#{sc_index} #{success_criterion.title}" - rich_text success_criterion.description_html - rich_text success_criterion.comment + success_criteria.each.with_index(1) do |success_criterion, sc_index| + success_criterion_row(success_criterion, [element_index, sc_index]) end end end + + private + + def success_criterion_row(success_criterion, index) + heading3 "#{index.join(".")} #{success_criterion.title}" + safe_display(success_criterion.test_comment) do + heading4 "Kommentar" + rich_text success_criterion.test_comment + end + safe_display(success_criterion.quick_criterion) do + heading4 "Kriterium" + rich_text success_criterion.quick_criterion + end + safe_display(success_criterion.quick_fail) do + heading4 "Fail" + rich_text success_criterion.quick_fail + end + safe_display(success_criterion.quick_fix) do + heading4 "Fix" + rich_text success_criterion.quick_fix + end + end end end diff --git a/app/models/report.rb b/app/models/report.rb index 7b15df9..2a03b91 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -3,7 +3,20 @@ class Report < ApplicationRecord has_many :pages, -> { order(:position) }, dependent: :destroy has_many :elements, through: :pages, dependent: :destroy + has_many :success_criteria, through: :elements, dependent: :destroy + has_rich_text :comment validates :name, presence: true + + def export + export_success_criteria = success_criteria.failed + export_elements = export_success_criteria.group_by(&:element) + export_pages = export_elements.group_by { |k, v| k } + { + pages: export_pages, + elements: export_elements, + success_criteria: export_success_criteria + } + end end diff --git a/app/models/success_criterion.rb b/app/models/success_criterion.rb index e575308..afe275a 100644 --- a/app/models/success_criterion.rb +++ b/app/models/success_criterion.rb @@ -19,6 +19,8 @@ class SuccessCriterion < ApplicationRecord validates :result, inclusion: { in: self.results.keys + [ nil ] } + scope :failed, -> { where(result: :failed) } + def level_value return nil unless level diff --git a/app/views/reports/show.html.slim b/app/views/reports/show.html.slim index 3fc7196..9533137 100644 --- a/app/views/reports/show.html.slim +++ b/app/views/reports/show.html.slim @@ -43,9 +43,9 @@ div = link_to(report_export_path(@report), class: "btn btn-secondary", target: :_blank) do i.bi.bi-filetype-html | Online HTML - / = link_to report_path(@report, format: :pdf), class: "btn btn-secondary", target: "_blank" do - / i.bi.bi-filetype-pdf - / | PDF + = link_to report_path(@report, format: :pdf), class: "btn btn-secondary", target: "_blank" do + i.bi.bi-filetype-pdf + | PDF / = link_to report_path(@report, format: :docx), class: "btn btn-secondary", target: "_blank" do / i.bi.bi-filetype-docx / | DOCX diff --git a/app/views/success_criteria/_body.html.slim b/app/views/success_criteria/_body.html.slim index e0c4190..7da29c1 100644 --- a/app/views/success_criteria/_body.html.slim +++ b/app/views/success_criteria/_body.html.slim @@ -11,7 +11,10 @@ label.btn.btn-outline-secondary for=dom_id(success_criterion, :result_not_applicable) Nicht anwendbar /= form.radio_button_without_bootstrap :result, nil, class: "btn-check", autocomplete: "off", id: dom_id(success_criterion, :result_not_applicable) /label.btn.btn-outline-secondary for=dom_id(success_criterion, :nil) Reset - / = dropdown_menu([{ text: "Bearbeiten", icon: "pencil", href: edit_success_criterion_path(success_criterion) }, { text: "Löschen", icon: "trash", href: success_criterion, color: :danger, method: :delete, confirm: "Bist du sicher?"}], klass: "mt-3 ms-auto") + - unless success_criterion.test_comment.blank? + = link_to(edit_comment_success_criterion_path(success_criterion), class: "btn btn-outline-warning my-3 ms-3", data: { turbo_frame: "modal" }) do + i.bi.bi-chat> + 'Kommentar bearbeiten = success_criterion_menu(success_criterion) .row .col diff --git a/app/views/success_criteria/edit_comment.html.slim b/app/views/success_criteria/edit_comment.html.slim new file mode 100644 index 0000000..391a34f --- /dev/null +++ b/app/views/success_criteria/edit_comment.html.slim @@ -0,0 +1,5 @@ += bootstrap_form_with(model: @success_criterion.persisted? ? @success_criterion : [:element, @success_criterion], data: { controller: "unsaved-changes" }) do |form| + = form.rich_text_area :test_comment + = form.submit class: "btn btn-primary" +- unless modal? + =< link_to "Abbrechen", @success_criterion.persisted? ? @success_criterion : @success_criterion.element, class: "btn btn-outline-secondary" diff --git a/config/routes.rb b/config/routes.rb index 9dcb17e..fd5e5e1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -19,6 +19,10 @@ Rails.application.routes.draw do get "from_checklist", action: :new_from_checklist, as: :new_from_checklist post "from_checklist", action: :create_from_checklist, as: :create_from_checklist end + + member do + get "edit_comment" + end end end end From fd11eaff33ac97c283ee3a37ac55d846aae86e67 Mon Sep 17 00:00:00 2001 From: david Date: Sat, 23 Nov 2024 22:05:01 +0100 Subject: [PATCH 26/46] Add screenshot image to pdf export --- app/models/pdf_documents/base.rb | 6 +++++- app/models/pdf_documents/customer_report.rb | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/app/models/pdf_documents/base.rb b/app/models/pdf_documents/base.rb index c34aa80..4242a57 100644 --- a/app/models/pdf_documents/base.rb +++ b/app/models/pdf_documents/base.rb @@ -98,7 +98,7 @@ module PdfDocuments def safe_display(value, &block) return if value.blank? - yield + yield(value) end def bold(text) @@ -106,5 +106,9 @@ module PdfDocuments @prawn_document.text text end end + + def image(attachable, **args) + @prawn_document.image ActiveStorage::Blob.service.path_for(attachable.key), **args + end end end diff --git a/app/models/pdf_documents/customer_report.rb b/app/models/pdf_documents/customer_report.rb index 694642a..1da4ad9 100644 --- a/app/models/pdf_documents/customer_report.rb +++ b/app/models/pdf_documents/customer_report.rb @@ -19,6 +19,7 @@ module PdfDocuments bold("Pfad: #{element.page.path}") move_down(5) rich_text element.description + safe_display(element.screenshot) { image(_1, height: 160) } success_criteria.each.with_index(1) do |success_criterion, sc_index| success_criterion_row(success_criterion, [element_index, sc_index]) From 02e13871bb060ae3dfdb73cc17acfbf2713396ba Mon Sep 17 00:00:00 2001 From: david Date: Sat, 23 Nov 2024 22:06:50 +0100 Subject: [PATCH 27/46] Try fix faulty images --- app/models/pdf_documents/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/pdf_documents/base.rb b/app/models/pdf_documents/base.rb index 4242a57..8c5a4f1 100644 --- a/app/models/pdf_documents/base.rb +++ b/app/models/pdf_documents/base.rb @@ -108,7 +108,7 @@ module PdfDocuments end def image(attachable, **args) - @prawn_document.image ActiveStorage::Blob.service.path_for(attachable.key), **args + @prawn_document.image(ActiveStorage::Blob.service.path_for(attachable.key), **args) rescue StandardError end end end From 3f76aeeb3c8a31383d0b0dbc0666d32929d35437 Mon Sep 17 00:00:00 2001 From: david Date: Sat, 23 Nov 2024 23:12:06 +0100 Subject: [PATCH 28/46] improve pdf --- Dockerfile | 2 +- app/models/pdf_documents/base.rb | 45 +++++++++++++++++---- app/models/pdf_documents/customer_report.rb | 41 ++++++++++++------- 3 files changed, 65 insertions(+), 23 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0e930c6..c502e7d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -124,7 +124,7 @@ timeout = 3 [language-server.ruby-lsp] command = "ruby-lsp" -args = ["--debug"] +# args = ["--debug"] LANGS diff --git a/app/models/pdf_documents/base.rb b/app/models/pdf_documents/base.rb index 8c5a4f1..8a50bdd 100644 --- a/app/models/pdf_documents/base.rb +++ b/app/models/pdf_documents/base.rb @@ -60,12 +60,12 @@ module PdfDocuments def markup_options { text: { size: 12, margin_bottom: 5 }, - heading1: { style: :bold, size: 26, margin_bottom: 10, margin_top: 0 }, - heading2: { style: :bold, size: 17, margin_bottom: 10, margin_top: 15 }, - heading3: { style: :bold, size: 13, margin_bottom: 10, margin_top: 15 }, - heading4: { style: :bold, size: 12, margin_bottom: 10, margin_top: 10 }, - heading5: { style: :bold, size: 12, margin_bottom: 10, margin_top: 10 }, - heading6: { style: :thin, size: 12, margin_bottom: 10, margin_top: 5 } + heading1: { style: :bold, size: 26, margin_bottom: 5, margin_top: 0 }, + heading2: { style: :bold, size: 17, margin_bottom: 5, margin_top: 5 }, + heading3: { style: :bold, size: 13, margin_bottom: 5, margin_top: 5 }, + heading4: { style: :bold, size: 12, margin_bottom: 5, margin_top: 5 }, + heading5: { style: :bold, size: 12, margin_bottom: 5, margin_top: 5 }, + heading6: { style: :thin, size: 12, margin_bottom: 5, margin_top: 5 } } end @@ -80,7 +80,7 @@ module PdfDocuments rich_text = rich_text.to_s.gsub("", "") end - rich_text + rich_text.sub(/(
)+$/, "") end def font(...) @@ -108,7 +108,36 @@ module PdfDocuments end def image(attachable, **args) - @prawn_document.image(ActiveStorage::Blob.service.path_for(attachable.key), **args) rescue StandardError + @prawn_document.image(ActiveStorage::Blob.service.path_for(attachable.key), **args) rescue StandardError do false end + end + + def without_page_break(&block) + return yield + return @prawn_document.bounding_box([ 0, @prawn_document.cursor ], width: 300) do + yield + # @prawn_document.stroke_bounds if Rails.env.development? + # print_coordinates + end + @prawn_document.span(520) do + yield + # @prawn_document.stroke_bounds + end + end + + def print_coordinates + @prawn_document.text("top: #{@prawn_document.bounds.top}") + @prawn_document.text("bottom: #{@prawn_document.bounds.bottom}") + @prawn_document.text("left: #{@prawn_document.bounds.left}") + @prawn_document.text("right: #{@prawn_document.bounds.right}") + @prawn_document.move_down(10) + @prawn_document.text("absolute top: #{Float(@prawn_document.bounds.absolute_top).round(2)}") + @prawn_document.text("absolute bottom: #{Float(@prawn_document.bounds.absolute_bottom).round(2)}") + @prawn_document.text("absolute left: #{Float(@prawn_document.bounds.absolute_left).round(2)}") + @prawn_document.text("absolute right: #{Float(@prawn_document.bounds.absolute_right).round(2)}") + end + + def new_page + @prawn_document.start_new_page end end end diff --git a/app/models/pdf_documents/customer_report.rb b/app/models/pdf_documents/customer_report.rb index 1da4ad9..b3d6088 100644 --- a/app/models/pdf_documents/customer_report.rb +++ b/app/models/pdf_documents/customer_report.rb @@ -14,15 +14,17 @@ module PdfDocuments rich_text params.report.comment params.report.export[:elements].each.with_index(1) do |(element, success_criteria), element_index| - heading2 "#{element_index} #{element.title}" - move_down(5) - bold("Pfad: #{element.page.path}") - move_down(5) + new_page if element_index > 1 + without_page_break do + heading2 "#{element_index} #{element.title}" + bold("#{element.page.path}") + safe_display(element.screenshot) { image(_1.variant(:thumbnail), height: 160) && move_down(5) } + end rich_text element.description - safe_display(element.screenshot) { image(_1, height: 160) } + move_down(10) success_criteria.each.with_index(1) do |success_criterion, sc_index| - success_criterion_row(success_criterion, [element_index, sc_index]) + success_criterion_row(success_criterion, [ element_index, sc_index ]) end end end @@ -32,21 +34,32 @@ module PdfDocuments def success_criterion_row(success_criterion, index) heading3 "#{index.join(".")} #{success_criterion.title}" safe_display(success_criterion.test_comment) do - heading4 "Kommentar" - rich_text success_criterion.test_comment + without_page_break do + heading4 "Kommentar" + rich_text success_criterion.test_comment + end end safe_display(success_criterion.quick_criterion) do - heading4 "Kriterium" - rich_text success_criterion.quick_criterion + without_page_break do + heading4 "Kriterium" + rich_text success_criterion.quick_criterion + end end safe_display(success_criterion.quick_fail) do - heading4 "Fail" - rich_text success_criterion.quick_fail + without_page_break do + heading4 "Fail" + rich_text success_criterion.quick_fail + end end safe_display(success_criterion.quick_fix) do - heading4 "Fix" - rich_text success_criterion.quick_fix + without_page_break do + heading4 "Fix" + rich_text success_criterion.quick_fix + end end + heading4("Protokollnummer") + text(success_criterion.number) + move_down(10) end end end From bf2001d39c73c7cb7c8c28142118d7ef9f81e500 Mon Sep 17 00:00:00 2001 From: david Date: Sat, 23 Nov 2024 23:16:24 +0100 Subject: [PATCH 29/46] Fix unprocessable files --- app/models/pdf_documents/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/pdf_documents/base.rb b/app/models/pdf_documents/base.rb index 8a50bdd..6f9d090 100644 --- a/app/models/pdf_documents/base.rb +++ b/app/models/pdf_documents/base.rb @@ -98,7 +98,7 @@ module PdfDocuments def safe_display(value, &block) return if value.blank? - yield(value) + yield(value) rescue StandardError { nil } end def bold(text) From 2a8e2835f378ef86ee0cb20b7aee31a1ff76f1d5 Mon Sep 17 00:00:00 2001 From: david Date: Sat, 23 Nov 2024 23:19:06 +0100 Subject: [PATCH 30/46] Fix error handling --- app/models/pdf_documents/base.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/models/pdf_documents/base.rb b/app/models/pdf_documents/base.rb index 6f9d090..671e045 100644 --- a/app/models/pdf_documents/base.rb +++ b/app/models/pdf_documents/base.rb @@ -98,7 +98,11 @@ module PdfDocuments def safe_display(value, &block) return if value.blank? - yield(value) rescue StandardError { nil } + begin + yield(value) + rescue StandardError do + nil + end end def bold(text) From 89ef15f41d64660c86bad91c16b80de82ec6ffa5 Mon Sep 17 00:00:00 2001 From: david Date: Sat, 23 Nov 2024 23:26:21 +0100 Subject: [PATCH 31/46] Fix syntax --- app/models/pdf_documents/base.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/pdf_documents/base.rb b/app/models/pdf_documents/base.rb index 671e045..a165182 100644 --- a/app/models/pdf_documents/base.rb +++ b/app/models/pdf_documents/base.rb @@ -99,8 +99,8 @@ module PdfDocuments return if value.blank? begin - yield(value) - rescue StandardError do + yield(value) + rescue StandardError nil end end From 4abae91a5ad40926cefd43cd0fe60235a0706283 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 24 Nov 2024 00:58:36 +0100 Subject: [PATCH 32/46] Improve pdf --- app/models/pdf_documents/base.rb | 11 +++-- app/models/pdf_documents/customer_report.rb | 47 ++++++++++++++++++--- config/initializers/prawn.rb | 10 +++++ 3 files changed, 59 insertions(+), 9 deletions(-) create mode 100644 config/initializers/prawn.rb diff --git a/app/models/pdf_documents/base.rb b/app/models/pdf_documents/base.rb index a165182..32703da 100644 --- a/app/models/pdf_documents/base.rb +++ b/app/models/pdf_documents/base.rb @@ -71,7 +71,6 @@ module PdfDocuments def logo @prawn_document.image "app/assets/images/logo-apfelschule.png", width: 150 - @prawn_document.move_down 30 end def prepare_rich_text(rich_text) @@ -112,7 +111,11 @@ module PdfDocuments end def image(attachable, **args) - @prawn_document.image(ActiveStorage::Blob.service.path_for(attachable.key), **args) rescue StandardError do false end + begin + @prawn_document.image(ActiveStorage::Blob.service.path_for(attachable.key), **args) && raise("david") + rescue StandardError + false + end end def without_page_break(&block) @@ -140,8 +143,8 @@ module PdfDocuments @prawn_document.text("absolute right: #{Float(@prawn_document.bounds.absolute_right).round(2)}") end - def new_page - @prawn_document.start_new_page + def new_page(required_space: 0) + @prawn_document.start_new_page if @prawn_document.cursor <= required_space + 10 end end end diff --git a/app/models/pdf_documents/customer_report.rb b/app/models/pdf_documents/customer_report.rb index b3d6088..bb7311a 100644 --- a/app/models/pdf_documents/customer_report.rb +++ b/app/models/pdf_documents/customer_report.rb @@ -5,16 +5,31 @@ module PdfDocuments private def generate + @prawn_document.repeat(:all) do + # @prawn_document.bounding_box([ 0, 790 ], width: 200) do + # logo + # end + # @prawn_document.formatted_text_box([ { + # text: "Dieses Dokument wurd am #{Time.current.strftime('%d %B %Y')} um #{Time.current.strftime('%H:%M:%S')} erstellt.", + # size: 8, + # align: :right + # } ], + # at: [ 0, -15 ], + # width: 200 + # ) + @prawn_document.draw_text "#{I18n.l params.report.updated_at.to_date} #{params.report.name}", at: [ 0, 770 ] + @prawn_document.font_size(8) do + @prawn_document.draw_text("Dieses Dokument wurd am #{Time.current.strftime('%d %B %Y')} um #{Time.current.strftime('%H:%M:%S')} erstellt.", at: [ 0, -15 ]) + end + end logo - @prawn_document.formatted_text_box [ { - text: "Dieses Dokument wurd am #{Time.current.strftime('%d %B %Y')} um #{Time.current.strftime('%H:%M:%S')} erstellt.", size: 8, align: :right - } ], align: :right - + move_down 20 heading1 params.report.name rich_text params.report.comment + @pages = {} params.report.export[:elements].each.with_index(1) do |(element, success_criteria), element_index| - new_page if element_index > 1 + new_page(required_space: 200) if element_index > 1 without_page_break do heading2 "#{element_index} #{element.title}" bold("#{element.page.path}") @@ -24,9 +39,31 @@ module PdfDocuments move_down(10) success_criteria.each.with_index(1) do |success_criterion, sc_index| + @pages[success_criterion.id] = @prawn_document.page_number success_criterion_row(success_criterion, [ element_index, sc_index ]) end end + string = "Seite / " + # Green page numbers 1 to 7 + options = { + at: [ @prawn_document.bounds.right - 150, -10 ], + width: 150, + align: :right, + start_count_at: 1 + } + @prawn_document.number_pages string, options + + x = params + p = @pages + @prawn_document.outline.define do + x.report.export[:elements].each.with_index(1) do |(element, success_criteria), element_index| + section("#{element_index} #{element.title}") do + success_criteria.each.with_index(1) do |sc, sc_index| + page(title: "#{element_index}.#{sc_index} itle}", destination: p[sc.id]) + end + end + end + end end private diff --git a/config/initializers/prawn.rb b/config/initializers/prawn.rb new file mode 100644 index 0000000..41bf34b --- /dev/null +++ b/config/initializers/prawn.rb @@ -0,0 +1,10 @@ +PrawnRails.config do |config| + config.page_layout = :portrait + config.page_size = "A4" + config.skip_page_creation = false + config.left_margin = 48 + config.top_margin = 48 + config.right_margin = 48 + config.bottom_margin = 48 +end + From bf0548ecfea89cf9a60a9c965440632f5cbfbdb8 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 24 Nov 2024 01:32:24 +0100 Subject: [PATCH 33/46] Improve pdf more --- app/models/pdf_documents/customer_report.rb | 49 ++++++++++++--------- config/initializers/prawn.rb | 9 ++-- 2 files changed, 33 insertions(+), 25 deletions(-) diff --git a/app/models/pdf_documents/customer_report.rb b/app/models/pdf_documents/customer_report.rb index bb7311a..95a0154 100644 --- a/app/models/pdf_documents/customer_report.rb +++ b/app/models/pdf_documents/customer_report.rb @@ -5,25 +5,6 @@ module PdfDocuments private def generate - @prawn_document.repeat(:all) do - # @prawn_document.bounding_box([ 0, 790 ], width: 200) do - # logo - # end - # @prawn_document.formatted_text_box([ { - # text: "Dieses Dokument wurd am #{Time.current.strftime('%d %B %Y')} um #{Time.current.strftime('%H:%M:%S')} erstellt.", - # size: 8, - # align: :right - # } ], - # at: [ 0, -15 ], - # width: 200 - # ) - @prawn_document.draw_text "#{I18n.l params.report.updated_at.to_date} #{params.report.name}", at: [ 0, 770 ] - @prawn_document.font_size(8) do - @prawn_document.draw_text("Dieses Dokument wurd am #{Time.current.strftime('%d %B %Y')} um #{Time.current.strftime('%H:%M:%S')} erstellt.", at: [ 0, -15 ]) - end - end - logo - move_down 20 heading1 params.report.name rich_text params.report.comment @pages = {} @@ -53,13 +34,41 @@ module PdfDocuments } @prawn_document.number_pages string, options + @prawn_document.repeat(:all) do + # @prawn_document.bounding_box([ 0, 790 ], width: 200) do + # logo + # end + # @prawn_document.formatted_text_box([ { + # text: "Dieses Dokument wurd am #{Time.current.strftime('%d %B %Y')} um #{Time.current.strftime('%H:%M:%S')} erstellt.", + # size: 8, + # align: :right + # } ], + # at: [ 0, -15 ], + # width: 200 + # ) + @prawn_document.text_box "#{params.report.name}", at: [ 150, 777 ], inline_format: true, width: 300 + @prawn_document.text_box "#{I18n.l params.report.updated_at.to_date, format: :long}", at: [ 0, 777 ], inline_format: true, width: 516, align: :right + @prawn_document.bounding_box([-10, 800], width: 200, height: 200) do + logo + end + @prawn_document.font_size(8) do + @prawn_document.draw_text("Dieses Dokument wurd am #{Time.current.strftime('%d %B %Y')} um #{Time.current.strftime('%H:%M:%S')} erstellt.", at: [ 0, -15 ]) + end + @prawn_document.bounding_box([0, 0], width: 516) do + hr + end + @prawn_document.bounding_box([0, 766], width: 516) do + hr + end + end + x = params p = @pages @prawn_document.outline.define do x.report.export[:elements].each.with_index(1) do |(element, success_criteria), element_index| section("#{element_index} #{element.title}") do success_criteria.each.with_index(1) do |sc, sc_index| - page(title: "#{element_index}.#{sc_index} itle}", destination: p[sc.id]) + page(title: "#{element_index}.#{sc_index} #{sc.title}", destination: p[sc.id]) end end end diff --git a/config/initializers/prawn.rb b/config/initializers/prawn.rb index 41bf34b..7754ea7 100644 --- a/config/initializers/prawn.rb +++ b/config/initializers/prawn.rb @@ -2,9 +2,8 @@ PrawnRails.config do |config| config.page_layout = :portrait config.page_size = "A4" config.skip_page_creation = false - config.left_margin = 48 - config.top_margin = 48 - config.right_margin = 48 - config.bottom_margin = 48 + config.left_margin = 32 + config.top_margin = 64 + config.right_margin = 32 + config.bottom_margin = 32 end - From 976936d480c2758be93069e59cc9a1ecab2f624f Mon Sep 17 00:00:00 2001 From: david Date: Sun, 24 Nov 2024 03:16:36 +0100 Subject: [PATCH 34/46] Improve pdf --- app/assets/images/logo-apfelschule.png | Bin 13814 -> 13306 bytes app/assets/images/logo-apfelschule_xs.png | Bin 0 -> 2904 bytes app/models/pdf_documents/base.rb | 9 +- app/models/pdf_documents/customer_report.rb | 103 ++++++++++++++++---- 4 files changed, 90 insertions(+), 22 deletions(-) create mode 100644 app/assets/images/logo-apfelschule_xs.png diff --git a/app/assets/images/logo-apfelschule.png b/app/assets/images/logo-apfelschule.png index 475c455114dbe8acb63a83556a68e1159d208fb6..d603a48edd3b77a2086f6227a7eb5b6a7aef5cf2 100644 GIT binary patch literal 13306 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 Ef=b3fBme*a literal 13814 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 XM9W#sK&Q~(msqlrN)lCK#zFrJM`%Pi diff --git a/app/assets/images/logo-apfelschule_xs.png b/app/assets/images/logo-apfelschule_xs.png new file mode 100644 index 0000000000000000000000000000000000000000..d9d46ff0664698d5c22304caa50b23c8564d434d GIT binary patch literal 2904 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" end - def text(text) - @prawn_document.text text, markup_options[:text] + def text(text, **args) + @prawn_document.text text, markup_options[:text].merge(args) end def rich_text(text) @@ -69,8 +69,8 @@ module PdfDocuments } end - def logo - @prawn_document.image "app/assets/images/logo-apfelschule.png", width: 150 + def logo(width: 24, xs: true) + @prawn_document.image "app/assets/images/logo-apfelschule#{ xs ? "_xs" : "" }.png", width: end def prepare_rich_text(rich_text) @@ -81,6 +81,7 @@ module PdfDocuments rich_text.sub(/(
)+$/, "") end + # @prawn_document.draw_text("Dokument erstellt am #{Time.current.strftime('%d %B %Y')} um #{Time.current.strftime('%H:%M:%S')}", at: [ 0, -15 ]) def font(...) @prawn_document.font(...) diff --git a/app/models/pdf_documents/customer_report.rb b/app/models/pdf_documents/customer_report.rb index 95a0154..0cf4b92 100644 --- a/app/models/pdf_documents/customer_report.rb +++ b/app/models/pdf_documents/customer_report.rb @@ -5,16 +5,34 @@ module PdfDocuments private def generate + move_down 130 + logo(width: 250, xs: false) + move_down 40 heading1 params.report.name - rich_text params.report.comment + + move_down 20 + @prawn_document.text "#{I18n.l params.report.updated_at.to_date, format: :long}" + move_down 40 + safe_display(params.report.comment) do + rich_text _1 + move_down 30 + end + + @prawn_document.font_size(8) do + @prawn_document.draw_text("Dokument erstellt am #{Time.current.strftime('%d %B %Y')} um #{Time.current.strftime('%H:%M:%S')}", at: [0, 0]) + end + @prawn_document.start_new_page @pages = {} params.report.export[:elements].each.with_index(1) do |(element, success_criteria), element_index| new_page(required_space: 200) if element_index > 1 without_page_break do heading2 "#{element_index} #{element.title}" - bold("#{element.page.path}") - safe_display(element.screenshot) { image(_1.variant(:thumbnail), height: 160) && move_down(5) } + @prawn_document.text("Pfad: #{element.page.path}", inline_format: true) + safe_display(element.screenshot) do + image(_1.variant(:thumbnail), height: 160) + move_down(15) + end end rich_text element.description move_down(10) @@ -24,17 +42,71 @@ module PdfDocuments success_criterion_row(success_criterion, [ element_index, sc_index ]) end end + + @prawn_document.start_new_page + heading1("Anhang: Richtlinien") + params.report.export[:success_criteria].group_by(&:check).sort_by{ |c, _scs| c.external_number }.each do |check, criteria| + heading2(check.display_label) + { + external_number: { label: "WCAG Nummer" }, + external_url: { label: "WCAG Link" }, + conformity_level: { label: "Konformität" }, + conformity_notice_de: { label: "Anmerkung Konformität", rich: true }, + priority: { label: "Priorität" }, + criterion_de: { label: "Kriterium/Grundlage", rich: true }, + exemption_details_de: { label: "Ausnahmen", rich: true }, + criterion_details_de: { label: "Verstehen", rich: true }, + example_de: { label: "Beispiel", rich: true }, + annotation_de: { label: "Anmerkung", rich: true } + }.each do |attribute, options| + v = check.send(attribute) + safe_display(v) do + text("#{options[:label]}", inline_format: true) + if options[:rich] + rich_text(_1) + else + text(_1) + end + end + end + + if check.links.any? + text("Links", inline_format: true) + check.links.group_by(&:link_category).each do |cat, links| + text(%Q( + #{cat.name} +
    + #{links.map{ "#{_1.text}" }.join() } +
+ )) + end + end + + text("Erfolgskriterien", inline_format: true) + text criteria.map(&:number).sort.join(", ") + end string = "Seite / " # Green page numbers 1 to 7 options = { - at: [ @prawn_document.bounds.right - 150, -10 ], + at: [ @prawn_document.bounds.right - 150, 776 ], width: 150, align: :right, - start_count_at: 1 + start_count_at: 2, + page_filter: ->(x) { x > 1 } } @prawn_document.number_pages string, options - @prawn_document.repeat(:all) do + @prawn_document.repeat(2..) do + @prawn_document.text_box "#{params.report.name}", at: [ 50, 777 ], inline_format: true, width: 300 + # @prawn_document.text_box "#{I18n.l params.report.updated_at.to_date, format: :long}", at: [ 0, 777 ], inline_format: true, width: 532, align: :right + @prawn_document.bounding_box([0, 766], width: 532) do + hr + end + @prawn_document.bounding_box([0, 796], width: 200, height: 200) do + logo + end + end + @prawn_document.repeat(2..) do # @prawn_document.bounding_box([ 0, 790 ], width: 200) do # logo # end @@ -46,20 +118,13 @@ module PdfDocuments # at: [ 0, -15 ], # width: 200 # ) - @prawn_document.text_box "#{params.report.name}", at: [ 150, 777 ], inline_format: true, width: 300 - @prawn_document.text_box "#{I18n.l params.report.updated_at.to_date, format: :long}", at: [ 0, 777 ], inline_format: true, width: 516, align: :right - @prawn_document.bounding_box([-10, 800], width: 200, height: 200) do - logo - end + # @prawn_document.text_box "#{I18n.l params.report.updated_at.to_date, format: :long}", at: [ 0, 777 ], inline_format: true, width: 532, align: :right @prawn_document.font_size(8) do - @prawn_document.draw_text("Dieses Dokument wurd am #{Time.current.strftime('%d %B %Y')} um #{Time.current.strftime('%H:%M:%S')} erstellt.", at: [ 0, -15 ]) - end - @prawn_document.bounding_box([0, 0], width: 516) do - hr - end - @prawn_document.bounding_box([0, 766], width: 516) do - hr + # @prawn_document.draw_text("Dokument erstellt am #{Time.current.strftime('%d %B %Y')} um #{Time.current.strftime('%H:%M:%S')}", at: [ 0, -15 ]) end + # @prawn_document.bounding_box([0, 0], width: 532) do + # hr + # end end x = params @@ -105,6 +170,8 @@ module PdfDocuments end heading4("Protokollnummer") text(success_criterion.number) + heading4("WCAG") + text("#{success_criterion.check.external_number}'", inline_format: true) move_down(10) end end From 2de996a1949625e5f9deafc44db0448a892d1c28 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 24 Nov 2024 03:21:52 +0100 Subject: [PATCH 35/46] Fix link inline format true --- app/models/pdf_documents/customer_report.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/pdf_documents/customer_report.rb b/app/models/pdf_documents/customer_report.rb index 0cf4b92..1173006 100644 --- a/app/models/pdf_documents/customer_report.rb +++ b/app/models/pdf_documents/customer_report.rb @@ -78,7 +78,7 @@ module PdfDocuments
    #{links.map{ "#{_1.text}" }.join() }
- )) + ), inline_format: true) end end From 2082b20d5a045ea2dfc031eb7a9b4111640304c6 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 24 Nov 2024 03:22:51 +0100 Subject: [PATCH 36/46] list items --- app/models/pdf_documents/customer_report.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/pdf_documents/customer_report.rb b/app/models/pdf_documents/customer_report.rb index 1173006..1c060cf 100644 --- a/app/models/pdf_documents/customer_report.rb +++ b/app/models/pdf_documents/customer_report.rb @@ -76,7 +76,7 @@ module PdfDocuments text(%Q( #{cat.name}
    - #{links.map{ "#{_1.text}" }.join() } + #{links.map{ "
  • #{_1.text}
  • " }.join() }
), inline_format: true) end From e1dfb14d4da7a2f498a22bda872a6e5dca8f5857 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 24 Nov 2024 03:40:48 +0100 Subject: [PATCH 37/46] finish customer report pdf --- app/models/pdf_documents/customer_report.rb | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/app/models/pdf_documents/customer_report.rb b/app/models/pdf_documents/customer_report.rb index 1c060cf..e25b556 100644 --- a/app/models/pdf_documents/customer_report.rb +++ b/app/models/pdf_documents/customer_report.rb @@ -38,7 +38,7 @@ module PdfDocuments move_down(10) success_criteria.each.with_index(1) do |success_criterion, sc_index| - @pages[success_criterion.id] = @prawn_document.page_number + @pages[success_criterion] = @prawn_document.page_number success_criterion_row(success_criterion, [ element_index, sc_index ]) end end @@ -47,6 +47,7 @@ module PdfDocuments heading1("Anhang: Richtlinien") params.report.export[:success_criteria].group_by(&:check).sort_by{ |c, _scs| c.external_number }.each do |check, criteria| heading2(check.display_label) + @pages[check] = @prawn_document.page_number { external_number: { label: "WCAG Nummer" }, external_url: { label: "WCAG Link" }, @@ -71,14 +72,16 @@ module PdfDocuments end if check.links.any? + move_down 5 text("Links", inline_format: true) + move_down 5 check.links.group_by(&:link_category).each do |cat, links| - text(%Q( + rich_text(%Q( #{cat.name}
    - #{links.map{ "
  • #{_1.text}
  • " }.join() } + #{links.map{ "
  • #{_1.text}
  • " }.join() }
- ), inline_format: true) + )) end end @@ -133,10 +136,15 @@ module PdfDocuments x.report.export[:elements].each.with_index(1) do |(element, success_criteria), element_index| section("#{element_index} #{element.title}") do success_criteria.each.with_index(1) do |sc, sc_index| - page(title: "#{element_index}.#{sc_index} #{sc.title}", destination: p[sc.id]) + page(title: "#{element_index}.#{sc_index} #{sc.title}", destination: p[sc]) end end end + section("Anhang: Richtlinien") do + x.report.export[:success_criteria].group_by(&:check).sort_by{ |c, _scs| c.external_number }.each do |check, _criteria| + page(title: check.display_label, destination: p[check]) + end + end end end From 650bec3f577f1928d3ddcc260732019f76558bdc Mon Sep 17 00:00:00 2001 From: david Date: Sun, 24 Nov 2024 03:58:28 +0100 Subject: [PATCH 38/46] cosmetics --- app/models/pdf_documents/customer_report.rb | 23 +------------------ .../success_criteria/edit_comment.html.slim | 3 ++- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/app/models/pdf_documents/customer_report.rb b/app/models/pdf_documents/customer_report.rb index e25b556..c90ea54 100644 --- a/app/models/pdf_documents/customer_report.rb +++ b/app/models/pdf_documents/customer_report.rb @@ -101,7 +101,6 @@ module PdfDocuments @prawn_document.repeat(2..) do @prawn_document.text_box "#{params.report.name}", at: [ 50, 777 ], inline_format: true, width: 300 - # @prawn_document.text_box "#{I18n.l params.report.updated_at.to_date, format: :long}", at: [ 0, 777 ], inline_format: true, width: 532, align: :right @prawn_document.bounding_box([0, 766], width: 532) do hr end @@ -109,26 +108,6 @@ module PdfDocuments logo end end - @prawn_document.repeat(2..) do - # @prawn_document.bounding_box([ 0, 790 ], width: 200) do - # logo - # end - # @prawn_document.formatted_text_box([ { - # text: "Dieses Dokument wurd am #{Time.current.strftime('%d %B %Y')} um #{Time.current.strftime('%H:%M:%S')} erstellt.", - # size: 8, - # align: :right - # } ], - # at: [ 0, -15 ], - # width: 200 - # ) - # @prawn_document.text_box "#{I18n.l params.report.updated_at.to_date, format: :long}", at: [ 0, 777 ], inline_format: true, width: 532, align: :right - @prawn_document.font_size(8) do - # @prawn_document.draw_text("Dokument erstellt am #{Time.current.strftime('%d %B %Y')} um #{Time.current.strftime('%H:%M:%S')}", at: [ 0, -15 ]) - end - # @prawn_document.bounding_box([0, 0], width: 532) do - # hr - # end - end x = params p = @pages @@ -179,7 +158,7 @@ module PdfDocuments heading4("Protokollnummer") text(success_criterion.number) heading4("WCAG") - text("#{success_criterion.check.external_number}'", inline_format: true) + text("#{success_criterion.check.external_number}", inline_format: true) move_down(10) end end diff --git a/app/views/success_criteria/edit_comment.html.slim b/app/views/success_criteria/edit_comment.html.slim index 391a34f..46f1f06 100644 --- a/app/views/success_criteria/edit_comment.html.slim +++ b/app/views/success_criteria/edit_comment.html.slim @@ -1,5 +1,6 @@ +h2 Kommentar bearbeiten = bootstrap_form_with(model: @success_criterion.persisted? ? @success_criterion : [:element, @success_criterion], data: { controller: "unsaved-changes" }) do |form| - = form.rich_text_area :test_comment + = form.rich_text_area :test_comment, hide_label: true = form.submit class: "btn btn-primary" - unless modal? =< link_to "Abbrechen", @success_criterion.persisted? ? @success_criterion : @success_criterion.element, class: "btn btn-outline-secondary" From 83dd857776a6b85787beeafdc95abde4c0bc8936 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 24 Nov 2024 11:03:44 +0100 Subject: [PATCH 39/46] Fix some pdf errors --- app/models/pdf_documents/customer_report.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/models/pdf_documents/customer_report.rb b/app/models/pdf_documents/customer_report.rb index c90ea54..838dd45 100644 --- a/app/models/pdf_documents/customer_report.rb +++ b/app/models/pdf_documents/customer_report.rb @@ -77,9 +77,9 @@ module PdfDocuments move_down 5 check.links.group_by(&:link_category).each do |cat, links| rich_text(%Q( - #{cat.name} + #{cat.name}
    - #{links.map{ "
  • #{_1.text}
  • " }.join() } + #{links.map{ "
  • #{_1.text}
  • " }.join() }
)) end @@ -157,8 +157,10 @@ module PdfDocuments end heading4("Protokollnummer") text(success_criterion.number) - heading4("WCAG") - text("#{success_criterion.check.external_number}", inline_format: true) + safe_display(success_criterion.check.external_url) do + heading4("WCAG") + text("#{success_criterion.check.external_number || _1}", inline_format: true) + end move_down(10) end end From 0964187f227da1ddab8f2707c5a0b9408b007aea Mon Sep 17 00:00:00 2001 From: david Date: Sun, 24 Nov 2024 16:50:10 +0100 Subject: [PATCH 40/46] tidy --- app/controllers/concerns/authentication.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/controllers/concerns/authentication.rb b/app/controllers/concerns/authentication.rb index 771b21d..8cae6ea 100644 --- a/app/controllers/concerns/authentication.rb +++ b/app/controllers/concerns/authentication.rb @@ -21,7 +21,6 @@ module Authentication resume_session || request_authentication end - def resume_session Current.session ||= find_session_by_cookie end @@ -30,7 +29,6 @@ module Authentication Session.find_by(id: cookies.signed[:session_id]) end - def request_authentication session[:return_to_after_authenticating] = request.url redirect_to new_session_path @@ -40,7 +38,6 @@ module Authentication session.delete(:return_to_after_authenticating) || root_url end - def start_new_session_for(user) user.sessions.create!(user_agent: request.user_agent, ip_address: request.remote_ip).tap do |session| Current.session = session From 8b4ffb83ecf4af2ef8dd614baa4da21f419582ff Mon Sep 17 00:00:00 2001 From: david Date: Sun, 24 Nov 2024 22:08:36 +0100 Subject: [PATCH 41/46] Added projects --- app/controllers/application_controller.rb | 10 +- app/controllers/home_controller.rb | 5 - app/controllers/projects_controller.rb | 62 + app/controllers/reports_controller.rb | 54 +- app/helpers/projects_helper.rb | 2 + app/javascript/application.js | 1 - app/models/pdf_documents/customer_report.rb | 28 +- app/models/project.rb | 7 + app/models/report.rb | 2 + app/views/home/show.html.slim | 61 +- app/views/projects/_form.html.erb | 5 + app/views/projects/_project.html.slim | 20 + app/views/projects/_project.json.jbuilder | 2 + app/views/projects/edit.html.erb | 8 + app/views/projects/index.html.erb | 25 + app/views/projects/index.json.jbuilder | 1 + app/views/projects/new.html.erb | 7 + app/views/projects/show.html.erb | 9 + app/views/projects/show.json.jbuilder | 1 + app/views/reports/_form.html.erb | 3 +- app/views/reports/edit.html.erb | 2 +- app/views/reports/index.html.erb | 2 +- app/views/reports/new.html.erb | 2 +- app/views/reports/show.html.slim | 14 +- db/migrate/20241124183246_create_projects.rb | 9 + .../20241124183406_add_project_to_reports.rb | 5 + db/queue_schema.rb | 68 +- db/schema.rb | 27 +- package-lock.json | 1831 ----------------- package.json | 2 +- test/controllers/projects_controller_test.rb | 55 + test/controllers/reports_controller_test.rb | 8 +- test/fixtures/projects.yml | 7 + test/fixtures/reports.yml | 2 + test/models/project_test.rb | 7 + test/system/projects_test.rb | 47 + test/system/reports_test.rb | 4 +- 37 files changed, 470 insertions(+), 1935 deletions(-) create mode 100644 app/controllers/projects_controller.rb create mode 100644 app/helpers/projects_helper.rb create mode 100644 app/models/project.rb create mode 100644 app/views/projects/_form.html.erb create mode 100644 app/views/projects/_project.html.slim create mode 100644 app/views/projects/_project.json.jbuilder create mode 100644 app/views/projects/edit.html.erb create mode 100644 app/views/projects/index.html.erb create mode 100644 app/views/projects/index.json.jbuilder create mode 100644 app/views/projects/new.html.erb create mode 100644 app/views/projects/show.html.erb create mode 100644 app/views/projects/show.json.jbuilder create mode 100644 db/migrate/20241124183246_create_projects.rb create mode 100644 db/migrate/20241124183406_add_project_to_reports.rb delete mode 100644 package-lock.json create mode 100644 test/controllers/projects_controller_test.rb create mode 100644 test/fixtures/projects.yml create mode 100644 test/models/project_test.rb create mode 100644 test/system/projects_test.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 9c0c681..7a1bd32 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -22,9 +22,13 @@ class ApplicationController < ActionController::Base path: :root }, { - label: Report.model_name.human(count: 2), - icon: :'journal-text', - path: :reports }, + label: Project.model_name.human(count: 2), + icon: :'folder', + path: :projects }, + # { + # label: Report.model_name.human(count: 2), + # icon: :'journal-text', + # path: :reports }, { label: I18n.t("backoffice"), icon: :gear, diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 9d3293e..46103e2 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -20,11 +20,6 @@ class HomeController < ApplicationController path: profile_path, active: action_name == "profile" }, - # { - # label: "Passwort ändern", - # path: new_password_path, - # icon: :lock - # }, { label: "Logout", icon: :"box-arrow-right", diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb new file mode 100644 index 0000000..a61963c --- /dev/null +++ b/app/controllers/projects_controller.rb @@ -0,0 +1,62 @@ +class ProjectsController < ApplicationController + before_action :set_project, only: %i[ show edit update destroy ] + + # GET /projects + def index + @projects = Project.all + end + + # GET /projects/1 + def show + end + + # GET /projects/new + def new + @project = Project.new + end + + # GET /projects/1/edit + def edit + end + + # POST /projects + def create + @project = if params[:copy_from_id] + Project.find(params[:copy_from_id]).dup + else + Project.new(project_params) + end + + if @project.save + redirect_to @project, notice: "Project was successfully created." + else + render :new, status: :unprocessable_entity + end + end + + # PATCH/PUT /projects/1 + def update + if @project.update(project_params) + redirect_to @project, notice: "Project was successfully updated.", status: :see_other + else + render :edit, status: :unprocessable_entity + end + end + + # DELETE /projects/1 + def destroy + @project.destroy! + redirect_to projects_url, notice: "Project was successfully destroyed.", status: :see_other + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_project + @project = Project.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def project_params + params.require(:project).permit(:name, :details) + end +end diff --git a/app/controllers/reports_controller.rb b/app/controllers/reports_controller.rb index d1733d5..3ec1e8b 100644 --- a/app/controllers/reports_controller.rb +++ b/app/controllers/reports_controller.rb @@ -1,11 +1,12 @@ # frozen_string_literal: true class ReportsController < ApplicationController + before_action :set_project, only: %i[index new create] before_action :set_report, only: %i[show edit update destroy work] # GET /reports def index - @reports = Report.all + @reports = @project.reports end # GET /reports/1 @@ -85,7 +86,7 @@ class ReportsController < ApplicationController # GET /reports/new def new - @report = Report.new + @report = Report.new(project_id: @project.id) end # GET /reports/1/edit @@ -93,10 +94,45 @@ class ReportsController < ApplicationController # POST /reports def create - @report = Report.new(report_params) + success = if params[:copy_from_id] + original = Report.find(params[:copy_from_id]) + @report = original.dup + @report.pages = original.pages.map do |page| + page.dup.tap do |p| + p.elements = page.elements.map do |element| + element.dup.tap do |e| + e.success_criteria = element.success_criteria.map do |sc| + csc = sc.dup + SuccessCriterion.rich_text_association_names + .map { _1.to_s.sub(/^rich_text_/, "") } + .each do |a| + csc.send("#{a}=", sc.send(a).dup) + end + csc + end + e.screenshot = element.screenshot&.dup + Element.rich_text_association_names + .map { _1.to_s.sub(/^rich_text_/, "") } + .each do |a| + e.send("#{a}=", element.send(a).dup) + end + end + end + Page.rich_text_association_names + .map { _1.to_s.sub(/^rich_text_/, "") } + .each do |a| + p.send("#{a}=", page.send(a).dup) + end + end + end + @report.save + else + @report = Report.new(report_params.merge(project_id: @project.id)) + @report.save + end - if @report.save - redirect_to @report, notice: "Report was successfully created." + if success + redirect_to @report.reload, notice: "Report was successfully created." else render :new, status: :unprocessable_entity end @@ -114,7 +150,7 @@ class ReportsController < ApplicationController # DELETE /reports/1 def destroy @report.destroy! - redirect_to reports_url, notice: "Report was successfully destroyed.", status: :see_other + redirect_to project_url(@report.project), notice: "Report was successfully destroyed.", status: :see_other end def work @@ -130,7 +166,7 @@ class ReportsController < ApplicationController # Only allow a list of trusted parameters through. def report_params - params.require(:report).permit(:name, :comment, :url) + params.require(:report).permit(:name, :comment, :url, :project_id) end def filename(report, extension: "html") @@ -140,4 +176,8 @@ class ReportsController < ApplicationController def page_param params[:page_id] end + + def set_project + @project = Project.find(params[:project_id]) + end end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb new file mode 100644 index 0000000..db5c5ce --- /dev/null +++ b/app/helpers/projects_helper.rb @@ -0,0 +1,2 @@ +module ProjectsHelper +end diff --git a/app/javascript/application.js b/app/javascript/application.js index 4b59f32..ae46376 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -1,7 +1,6 @@ // Entry point for the build script in your package.json import "@hotwired/turbo-rails" import "./controllers" -import * as bootstrap from "bootstrap" import "trix" import "@rails/actiontext" diff --git a/app/models/pdf_documents/customer_report.rb b/app/models/pdf_documents/customer_report.rb index 838dd45..f64d510 100644 --- a/app/models/pdf_documents/customer_report.rb +++ b/app/models/pdf_documents/customer_report.rb @@ -13,13 +13,13 @@ module PdfDocuments move_down 20 @prawn_document.text "#{I18n.l params.report.updated_at.to_date, format: :long}" move_down 40 - safe_display(params.report.comment) do + safe_display(params.report.comment) do rich_text _1 move_down 30 end @prawn_document.font_size(8) do - @prawn_document.draw_text("Dokument erstellt am #{Time.current.strftime('%d %B %Y')} um #{Time.current.strftime('%H:%M:%S')}", at: [0, 0]) + @prawn_document.draw_text("Dokument erstellt am #{Time.current.strftime('%d %B %Y')} um #{Time.current.strftime('%H:%M:%S')}", at: [ 0, 0 ]) end @prawn_document.start_new_page @pages = {} @@ -29,9 +29,9 @@ module PdfDocuments without_page_break do heading2 "#{element_index} #{element.title}" @prawn_document.text("Pfad: #{element.page.path}", inline_format: true) - safe_display(element.screenshot) do - image(_1.variant(:thumbnail), height: 160) - move_down(15) + safe_display(element.screenshot) do + image(_1.variant(:thumbnail), height: 160) + move_down(15) end end rich_text element.description @@ -45,10 +45,10 @@ module PdfDocuments @prawn_document.start_new_page heading1("Anhang: Richtlinien") - params.report.export[:success_criteria].group_by(&:check).sort_by{ |c, _scs| c.external_number }.each do |check, criteria| + params.report.export[:success_criteria].group_by(&:check).sort_by { |c, _scs| c.external_number }.each do |check, criteria| heading2(check.display_label) @pages[check] = @prawn_document.page_number - { + { external_number: { label: "WCAG Nummer" }, external_url: { label: "WCAG Link" }, conformity_level: { label: "Konformität" }, @@ -61,12 +61,12 @@ module PdfDocuments annotation_de: { label: "Anmerkung", rich: true } }.each do |attribute, options| v = check.send(attribute) - safe_display(v) do + safe_display(v) do text("#{options[:label]}", inline_format: true) if options[:rich] rich_text(_1) - else - text(_1) + else + text(_1) end end end @@ -79,7 +79,7 @@ module PdfDocuments rich_text(%Q( #{cat.name} )) end @@ -101,10 +101,10 @@ module PdfDocuments @prawn_document.repeat(2..) do @prawn_document.text_box "#{params.report.name}", at: [ 50, 777 ], inline_format: true, width: 300 - @prawn_document.bounding_box([0, 766], width: 532) do + @prawn_document.bounding_box([ 0, 766 ], width: 532) do hr end - @prawn_document.bounding_box([0, 796], width: 200, height: 200) do + @prawn_document.bounding_box([ 0, 796 ], width: 200, height: 200) do logo end end @@ -120,7 +120,7 @@ module PdfDocuments end end section("Anhang: Richtlinien") do - x.report.export[:success_criteria].group_by(&:check).sort_by{ |c, _scs| c.external_number }.each do |check, _criteria| + x.report.export[:success_criteria].group_by(&:check).sort_by { |c, _scs| c.external_number }.each do |check, _criteria| page(title: check.display_label, destination: p[check]) end end diff --git a/app/models/project.rb b/app/models/project.rb new file mode 100644 index 0000000..2b7f03e --- /dev/null +++ b/app/models/project.rb @@ -0,0 +1,7 @@ +class Project < ApplicationRecord + has_many :reports, dependent: :restrict_with_error + + has_rich_text :details + + scope :current, -> { where("updated_at > ?", 1.month.ago) } +end diff --git a/app/models/report.rb b/app/models/report.rb index 2a03b91..68a36ab 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Report < ApplicationRecord + belongs_to :project, touch: true + has_many :pages, -> { order(:position) }, dependent: :destroy has_many :elements, through: :pages, dependent: :destroy has_many :success_criteria, through: :elements, dependent: :destroy diff --git a/app/views/home/show.html.slim b/app/views/home/show.html.slim index 345cd2b..1ab4d19 100644 --- a/app/views/home/show.html.slim +++ b/app/views/home/show.html.slim @@ -1,30 +1,37 @@ -h1 a11ydive -h2 Dashboard +h1 Dashboard -- if Report.any? - h3 Zuletzt bearbeitete Prüfberichte - ul - - Report.all.order(updated_at: :desc).limit(3).each do |r| - li = link_to(r.name, r) +.row + .col-md-6 + - if Project.current.any? + h2 + i.bi.bi-folder> + 'Aktuelle Projekte + ul + - Project.current.order(updated_at: :desc).each do |p| + li = link_to(p.name, p) -p - i.bi.bi-journal-text - =< Report.count - =< link_to Report.model_name.human(count: Report.count), :reports + - if Report.any? + h2 + i.bi.bi-journal-text> + 'Zuletzt bearbeitete Prüfberichte + ul + - Report.all.order(updated_at: :desc).limit(3).each do |r| + li = link_to(r.name, r) -h3 Hotkeys -p Auf der Bericht-Ausfüllen Seite können folgende Shortcuts verwendet werden: -dl - dt t - dd Springe zum Anfang des Contents (Skip-Link, kann auf allen Seiten verwendet werden) - dt a - dd Alle auf - dt s - dd Alle zu - dt b - dd Baum - dt n - dd Notizen - dt e - dd Springe zu erstem Check - \ No newline at end of file + .col-md-6 + h2 Hotkeys + p Auf der Bericht-Ausfüllen Seite können folgende Shortcuts verwendet werden: + dl + dt t + dd Springe zum Anfang des Contents (Skip-Link, kann auf allen Seiten verwendet werden) + dt a + dd Alle auf + dt s + dd Alle zu + dt b + dd Baum + dt n + dd Notizen + dt e + dd Springe zu erstem Check + diff --git a/app/views/projects/_form.html.erb b/app/views/projects/_form.html.erb new file mode 100644 index 0000000..5bbeee4 --- /dev/null +++ b/app/views/projects/_form.html.erb @@ -0,0 +1,5 @@ +<%= bootstrap_form_with(model: project) do |form| %> + <%= form.text_field :name %> + <%= form.rich_text_area :details %> + <%= form.submit %> +<% end %> diff --git a/app/views/projects/_project.html.slim b/app/views/projects/_project.html.slim new file mode 100644 index 0000000..5e098c9 --- /dev/null +++ b/app/views/projects/_project.html.slim @@ -0,0 +1,20 @@ +.project id=dom_id(project) + p + strong Name: + = project.name + p + strong Details: + = project.details + p + strong Berichte: + ul.ps-0 + - project.reports.each do + li.d-flex.ps-0 + =< "ID #{_1.id}: #{l(_1.created_at,format: :short)} #{_1.name}" + = link_to(_1, class: "btn btn-sm btn-link-secondary") do + i.bi.bi-folder-symlink + = button_to(project_reports_path(project), class: "btn btn-sm btn-link-secondary", params: { copy_from_id: _1.id }) do + i.bi.bi-copy + p + = link_to(new_project_report_path(project), class: "btn btn-secondary") do + i.bi.bi-plus-lg diff --git a/app/views/projects/_project.json.jbuilder b/app/views/projects/_project.json.jbuilder new file mode 100644 index 0000000..c1f8d9f --- /dev/null +++ b/app/views/projects/_project.json.jbuilder @@ -0,0 +1,2 @@ +json.extract! project, :id, :name, :created_at, :updated_at +json.url project_url(project, format: :json) diff --git a/app/views/projects/edit.html.erb b/app/views/projects/edit.html.erb new file mode 100644 index 0000000..ee36a94 --- /dev/null +++ b/app/views/projects/edit.html.erb @@ -0,0 +1,8 @@ +

<%= t("scaffold.pagetitle_edit", model: Project.model_name.human) %>

+ +<%= render "form", project: @project %> + +
+ <%= link_to t("scaffold.link_show", model: Project.model_name.human), @project %> + <%= link_to t("scaffold.link_index", model: Project.model_name.human(count: 2)), projects_path %> +
diff --git a/app/views/projects/index.html.erb b/app/views/projects/index.html.erb new file mode 100644 index 0000000..c487887 --- /dev/null +++ b/app/views/projects/index.html.erb @@ -0,0 +1,25 @@ +

<%= t("scaffold.pagetitle_index", model: Project.model_name.human(count: 2)) %>

+ + + + + + + + + + + <% @projects.each do |project| %> + + + + + + + <% end %> + +
<%= Project.human_attribute_name(:id) %><%= Project.human_attribute_name(:name) %>
<%= link_to(project.id, url_for(project)) %><%= link_to(project.name, url_for(project)) %>
+ +
+ <%= link_to t("scaffold.link_new", model: Project.model_name.human), new_project_path %> +
\ No newline at end of file diff --git a/app/views/projects/index.json.jbuilder b/app/views/projects/index.json.jbuilder new file mode 100644 index 0000000..e9db4f9 --- /dev/null +++ b/app/views/projects/index.json.jbuilder @@ -0,0 +1 @@ +json.array! @projects, partial: "projects/project", as: :project diff --git a/app/views/projects/new.html.erb b/app/views/projects/new.html.erb new file mode 100644 index 0000000..4876968 --- /dev/null +++ b/app/views/projects/new.html.erb @@ -0,0 +1,7 @@ +

<%= t("scaffold.pagetitle_new", model: Project.model_name.human) %>

+ +<%= render "form", project: @project %> + +
+ <%= link_to t("scaffold.link_index", model: Project.model_name.human(count: 2)), projects_path %> +
diff --git a/app/views/projects/show.html.erb b/app/views/projects/show.html.erb new file mode 100644 index 0000000..16d830f --- /dev/null +++ b/app/views/projects/show.html.erb @@ -0,0 +1,9 @@ +

<%= t("scaffold.pagetitle_show", model: @project.class.model_name.human) %>

+ +<%= render @project %> + +
+ <%= link_to t("scaffold.link_edit", model: @project.model_name.human), edit_project_path(@project) %> + <%= link_to t("scaffold.link_index", model: @project.model_name.human(count: 2)), projects_path %> + <%= button_to t("scaffold.link_destroy", model: @project.model_name.human), @project, method: :delete, class: "btn btn-outline-danger" if @project.reports.none? %> +
diff --git a/app/views/projects/show.json.jbuilder b/app/views/projects/show.json.jbuilder new file mode 100644 index 0000000..0be99bd --- /dev/null +++ b/app/views/projects/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! "projects/project", project: @project diff --git a/app/views/reports/_form.html.erb b/app/views/reports/_form.html.erb index 23a58a6..bea1172 100644 --- a/app/views/reports/_form.html.erb +++ b/app/views/reports/_form.html.erb @@ -1,4 +1,5 @@ -<%= bootstrap_form_with(model: report) do |form| %> +<%= bootstrap_form_with(model: report.persisted? ? report : [@project, report]) do |form| %> + <%= form.collection_select :project_id, Project.all, :id, :name, include_blank: true %> <%= form.text_field :name %> <%= form.text_field :url %> <%= form.rich_text_area :comment %> diff --git a/app/views/reports/edit.html.erb b/app/views/reports/edit.html.erb index 40ee203..7c43fd4 100644 --- a/app/views/reports/edit.html.erb +++ b/app/views/reports/edit.html.erb @@ -4,5 +4,5 @@
<%= link_to t("scaffold.link_show", model: Report.model_name.human), @report %> - <%= link_to t("scaffold.link_index", model: Report.model_name.human(count: 2)), reports_path %> + <%= link_to t("scaffold.link_index", model: Report.model_name.human(count: 2)), project_reports_path(@report.project) %>
diff --git a/app/views/reports/index.html.erb b/app/views/reports/index.html.erb index 61b1a89..af5baf5 100644 --- a/app/views/reports/index.html.erb +++ b/app/views/reports/index.html.erb @@ -27,5 +27,5 @@
- <%= link_to t("scaffold.link_new", model: Report.model_name.human), new_report_path %> + <%= link_to t("scaffold.link_new", model: Report.model_name.human), new_project_report_path(@project) %>
diff --git a/app/views/reports/new.html.erb b/app/views/reports/new.html.erb index cc21f9b..61d6d54 100644 --- a/app/views/reports/new.html.erb +++ b/app/views/reports/new.html.erb @@ -3,5 +3,5 @@ <%= render "form", report: @report %>
- <%= link_to t("scaffold.link_index", model: Report.model_name.human(count: 2)), reports_path %> + <%= link_to t("scaffold.link_index", model: Report.model_name.human(count: 2)), project_reports_path(@project) %>
diff --git a/app/views/reports/show.html.slim b/app/views/reports/show.html.slim index 9533137..03cce27 100644 --- a/app/views/reports/show.html.slim +++ b/app/views/reports/show.html.slim @@ -3,11 +3,12 @@ i.bi.bi-journal-text.me-2 = @report.name div - small - 'Erstellt am - = l(@report.created_at, format: :short) - ', zuletzt bearbeitet am - = l(@report.updated_at, format: :short) + small = "Erstellt am #{l(@report.created_at, format: :short)}, zuletzt bearbeitet am #{l(@report.updated_at, format: :short)}" + +p + i.bi.bi-folder + strong< = link_to(@report.project.name, @report.project) + .smb-4.lead.mb-5 = @report.comment || tag.i("leer") - if @current_page @@ -26,7 +27,6 @@ div .btn-group.me-3.visually-hidden = link_to("Alle zu [s]", "#", data: { action: "click->details-list#closeAll", controller: :hotkey, hotkey: "s" }, class: "btn btn-outline-secondary") = link_to("Alle auf [a]", "#", data: { action: "click->details-list#openAll", controller: :hotkey, hotkey: "a" }, class: "btn btn-outline-secondary") - /= button_to(tag.i(class: "bi bi-trash"), page_path(@current_page), method: :delete, class: "btn btn-outline-danger", form: {data: { turbo_confirm: "Bist du sicher?" }}) .row .col-lg-3.col-md-4.col-sm-12 .page_nav.sticky-top @@ -63,5 +63,5 @@ div | ODT .action-row = link_to t("scaffold.link_edit", model: @report.model_name.human), edit_report_path(@report) - = link_to t("scaffold.link_index", model: @report.model_name.human(count: 2)), reports_path + = link_to t("scaffold.link_index", model: @report.model_name.human(count: 2)), project_reports_path(@report.project) = button_to t("scaffold.link_destroy", model: @report.model_name.human), @report, method: :delete, class: "btn btn-outline-danger", data: { turbo_confirm: "Bist du sicher?"} diff --git a/db/migrate/20241124183246_create_projects.rb b/db/migrate/20241124183246_create_projects.rb new file mode 100644 index 0000000..b2dbe30 --- /dev/null +++ b/db/migrate/20241124183246_create_projects.rb @@ -0,0 +1,9 @@ +class CreateProjects < ActiveRecord::Migration[8.0] + def change + create_table :projects do |t| + t.string :name + + t.timestamps + end + end +end diff --git a/db/migrate/20241124183406_add_project_to_reports.rb b/db/migrate/20241124183406_add_project_to_reports.rb new file mode 100644 index 0000000..e840cdf --- /dev/null +++ b/db/migrate/20241124183406_add_project_to_reports.rb @@ -0,0 +1,5 @@ +class AddProjectToReports < ActiveRecord::Migration[8.0] + def change + add_reference :reports, :project, null: true, foreign_key: true + end +end diff --git a/db/queue_schema.rb b/db/queue_schema.rb index 85194b6..4b2cdcd 100644 --- a/db/queue_schema.rb +++ b/db/queue_schema.rb @@ -1,4 +1,16 @@ -ActiveRecord::Schema[7.1].define(version: 1) do +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# This file is the source Rails uses to define your schema when running `bin/rails +# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# be faster and is potentially less error prone than running all of your +# migrations from scratch. Old migrations may fail to apply correctly if those +# migrations use external dependencies or application code. +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema[8.0].define(version: 1) do create_table "solid_queue_blocked_executions", force: :cascade do |t| t.bigint "job_id", null: false t.string "queue_name", null: false @@ -6,24 +18,24 @@ ActiveRecord::Schema[7.1].define(version: 1) do t.string "concurrency_key", null: false t.datetime "expires_at", null: false t.datetime "created_at", null: false - t.index [ "concurrency_key", "priority", "job_id" ], name: "index_solid_queue_blocked_executions_for_release" - t.index [ "expires_at", "concurrency_key" ], name: "index_solid_queue_blocked_executions_for_maintenance" - t.index [ "job_id" ], name: "index_solid_queue_blocked_executions_on_job_id", unique: true + t.index ["concurrency_key", "priority", "job_id"], name: "index_solid_queue_blocked_executions_for_release" + t.index ["expires_at", "concurrency_key"], name: "index_solid_queue_blocked_executions_for_maintenance" + t.index ["job_id"], name: "index_solid_queue_blocked_executions_on_job_id", unique: true end create_table "solid_queue_claimed_executions", force: :cascade do |t| t.bigint "job_id", null: false t.bigint "process_id" t.datetime "created_at", null: false - t.index [ "job_id" ], name: "index_solid_queue_claimed_executions_on_job_id", unique: true - t.index [ "process_id", "job_id" ], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id" + t.index ["job_id"], name: "index_solid_queue_claimed_executions_on_job_id", unique: true + t.index ["process_id", "job_id"], name: "index_solid_queue_claimed_executions_on_process_id_and_job_id" end create_table "solid_queue_failed_executions", force: :cascade do |t| t.bigint "job_id", null: false t.text "error" t.datetime "created_at", null: false - t.index [ "job_id" ], name: "index_solid_queue_failed_executions_on_job_id", unique: true + t.index ["job_id"], name: "index_solid_queue_failed_executions_on_job_id", unique: true end create_table "solid_queue_jobs", force: :cascade do |t| @@ -37,17 +49,17 @@ ActiveRecord::Schema[7.1].define(version: 1) do t.string "concurrency_key" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.index [ "active_job_id" ], name: "index_solid_queue_jobs_on_active_job_id" - t.index [ "class_name" ], name: "index_solid_queue_jobs_on_class_name" - t.index [ "finished_at" ], name: "index_solid_queue_jobs_on_finished_at" - t.index [ "queue_name", "finished_at" ], name: "index_solid_queue_jobs_for_filtering" - t.index [ "scheduled_at", "finished_at" ], name: "index_solid_queue_jobs_for_alerting" + t.index ["active_job_id"], name: "index_solid_queue_jobs_on_active_job_id" + t.index ["class_name"], name: "index_solid_queue_jobs_on_class_name" + t.index ["finished_at"], name: "index_solid_queue_jobs_on_finished_at" + t.index ["queue_name", "finished_at"], name: "index_solid_queue_jobs_for_filtering" + t.index ["scheduled_at", "finished_at"], name: "index_solid_queue_jobs_for_alerting" end create_table "solid_queue_pauses", force: :cascade do |t| t.string "queue_name", null: false t.datetime "created_at", null: false - t.index [ "queue_name" ], name: "index_solid_queue_pauses_on_queue_name", unique: true + t.index ["queue_name"], name: "index_solid_queue_pauses_on_queue_name", unique: true end create_table "solid_queue_processes", force: :cascade do |t| @@ -59,9 +71,9 @@ ActiveRecord::Schema[7.1].define(version: 1) do t.text "metadata" t.datetime "created_at", null: false t.string "name", null: false - t.index [ "last_heartbeat_at" ], name: "index_solid_queue_processes_on_last_heartbeat_at" - t.index [ "name", "supervisor_id" ], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true - t.index [ "supervisor_id" ], name: "index_solid_queue_processes_on_supervisor_id" + t.index ["last_heartbeat_at"], name: "index_solid_queue_processes_on_last_heartbeat_at" + t.index ["name", "supervisor_id"], name: "index_solid_queue_processes_on_name_and_supervisor_id", unique: true + t.index ["supervisor_id"], name: "index_solid_queue_processes_on_supervisor_id" end create_table "solid_queue_ready_executions", force: :cascade do |t| @@ -69,9 +81,9 @@ ActiveRecord::Schema[7.1].define(version: 1) do t.string "queue_name", null: false t.integer "priority", default: 0, null: false t.datetime "created_at", null: false - t.index [ "job_id" ], name: "index_solid_queue_ready_executions_on_job_id", unique: true - t.index [ "priority", "job_id" ], name: "index_solid_queue_poll_all" - t.index [ "queue_name", "priority", "job_id" ], name: "index_solid_queue_poll_by_queue" + t.index ["job_id"], name: "index_solid_queue_ready_executions_on_job_id", unique: true + t.index ["priority", "job_id"], name: "index_solid_queue_poll_all" + t.index ["queue_name", "priority", "job_id"], name: "index_solid_queue_poll_by_queue" end create_table "solid_queue_recurring_executions", force: :cascade do |t| @@ -79,8 +91,8 @@ ActiveRecord::Schema[7.1].define(version: 1) do t.string "task_key", null: false t.datetime "run_at", null: false t.datetime "created_at", null: false - t.index [ "job_id" ], name: "index_solid_queue_recurring_executions_on_job_id", unique: true - t.index [ "task_key", "run_at" ], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true + t.index ["job_id"], name: "index_solid_queue_recurring_executions_on_job_id", unique: true + t.index ["task_key", "run_at"], name: "index_solid_queue_recurring_executions_on_task_key_and_run_at", unique: true end create_table "solid_queue_recurring_tasks", force: :cascade do |t| @@ -95,8 +107,8 @@ ActiveRecord::Schema[7.1].define(version: 1) do t.text "description" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.index [ "key" ], name: "index_solid_queue_recurring_tasks_on_key", unique: true - t.index [ "static" ], name: "index_solid_queue_recurring_tasks_on_static" + t.index ["key"], name: "index_solid_queue_recurring_tasks_on_key", unique: true + t.index ["static"], name: "index_solid_queue_recurring_tasks_on_static" end create_table "solid_queue_scheduled_executions", force: :cascade do |t| @@ -105,8 +117,8 @@ ActiveRecord::Schema[7.1].define(version: 1) do t.integer "priority", default: 0, null: false t.datetime "scheduled_at", null: false t.datetime "created_at", null: false - t.index [ "job_id" ], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true - t.index [ "scheduled_at", "priority", "job_id" ], name: "index_solid_queue_dispatch_all" + t.index ["job_id"], name: "index_solid_queue_scheduled_executions_on_job_id", unique: true + t.index ["scheduled_at", "priority", "job_id"], name: "index_solid_queue_dispatch_all" end create_table "solid_queue_semaphores", force: :cascade do |t| @@ -115,9 +127,9 @@ ActiveRecord::Schema[7.1].define(version: 1) do t.datetime "expires_at", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.index [ "expires_at" ], name: "index_solid_queue_semaphores_on_expires_at" - t.index [ "key", "value" ], name: "index_solid_queue_semaphores_on_key_and_value" - t.index [ "key" ], name: "index_solid_queue_semaphores_on_key", unique: true + t.index ["expires_at"], name: "index_solid_queue_semaphores_on_expires_at" + t.index ["key", "value"], name: "index_solid_queue_semaphores_on_key_and_value" + t.index ["key"], name: "index_solid_queue_semaphores_on_key", unique: true end add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade diff --git a/db/schema.rb b/db/schema.rb index 7623c6e..754674b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,19 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2024_11_11_034826) do +ActiveRecord::Schema[8.0].define(version: 2024_11_24_183406) do + create_table "account_remember_keys", force: :cascade do |t| + t.string "key", null: false + t.datetime "deadline", null: false + end + + create_table "accounts", force: :cascade do |t| + t.integer "status", default: 1, null: false + t.string "email", null: false + t.string "password_hash" + t.index ["email"], name: "index_accounts_on_email", unique: true, where: "status IN (1, 2)" + end + create_table "action_text_rich_texts", force: :cascade do |t| t.string "name", null: false t.text "body" @@ -115,6 +127,7 @@ ActiveRecord::Schema[8.0].define(version: 2024_11_11_034826) do t.datetime "updated_at", null: false t.integer "page_id", null: false t.integer "position", null: false + t.index ["page_id", "position"], name: "index_elements_on_page_id_and_position", unique: true t.index ["page_id"], name: "index_elements_on_page_id" end @@ -144,6 +157,7 @@ ActiveRecord::Schema[8.0].define(version: 2024_11_11_034826) do t.datetime "created_at", null: false t.datetime "updated_at", null: false t.text "notes" + t.index ["report_id", "position"], name: "index_pages_on_report_id_and_position", unique: true t.index ["report_id"], name: "index_pages_on_report_id" end @@ -154,11 +168,19 @@ ActiveRecord::Schema[8.0].define(version: 2024_11_11_034826) do t.datetime "updated_at", null: false end + create_table "projects", force: :cascade do |t| + t.string "name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "reports", force: :cascade do |t| t.string "name" t.datetime "created_at", null: false t.datetime "updated_at", null: false t.string "url" + t.integer "project_id" + t.index ["project_id"], name: "index_reports_on_project_id" end create_table "sessions", force: :cascade do |t| @@ -191,6 +213,7 @@ ActiveRecord::Schema[8.0].define(version: 2024_11_11_034826) do t.integer "priority" t.integer "position", null: false t.index ["check_id"], name: "index_success_criteria_on_check_id" + t.index ["element_id", "position"], name: "index_success_criteria_on_element_id_and_position", unique: true t.index ["element_id"], name: "index_success_criteria_on_element_id" end @@ -202,6 +225,7 @@ ActiveRecord::Schema[8.0].define(version: 2024_11_11_034826) do t.index ["email_address"], name: "index_users_on_email_address", unique: true end + add_foreign_key "account_remember_keys", "accounts", column: "id" add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "checklist_entries", "checklists" @@ -210,6 +234,7 @@ ActiveRecord::Schema[8.0].define(version: 2024_11_11_034826) do add_foreign_key "elements", "pages" add_foreign_key "links", "link_categories" add_foreign_key "pages", "reports" + add_foreign_key "reports", "projects" add_foreign_key "sessions", "users" add_foreign_key "success_criteria", "checks" add_foreign_key "success_criteria", "elements" diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 00e45c0..0000000 --- a/package-lock.json +++ /dev/null @@ -1,1831 +0,0 @@ -{ - "name": "app", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "app", - "dependencies": { - "@github/hotkey": "^3.1.1", - "@hotwired/stimulus": "^3.2.2", - "@hotwired/turbo-rails": "^8.0.4", - "@popperjs/core": "^2.11.8", - "@rails/actiontext": "^7.1.3-4", - "@rails/request.js": "^0.0.11", - "@stimulus-components/lightbox": "^4.0.0", - "autoprefixer": "^10.4.19", - "bootstrap": "^5.3.3", - "bootstrap-icons": "^1.11.3", - "esbuild": "^0.23.0", - "nodemon": "^3.1.4", - "postcss": "^8.4.39", - "postcss-cli": "^11.0.0", - "sass": "^1.77.8", - "sortablejs": "^1.15.3", - "trix": "^2.1.3" - }, - "devDependencies": { - "tabby-agent": "^1.7.0", - "vscode-css-languageserver-bin": "^1.4.0", - "vscode-langservers-extracted": "^4.10.0" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.0.tgz", - "integrity": "sha512-a3pMQhUEJkITgAw6e0bWA+F+vFtCciMjW/LPtoj99MhVt+Mfb6bbL9hu2wmTZgNd994qTAEw+U/r6k3qHWWaOQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@github/hotkey": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@github/hotkey/-/hotkey-3.1.1.tgz", - "integrity": "sha512-H30I6XDO3gFSgLuEuHoMBRZG9c3uCKNdAcYklL1FaZDPdU1bXfgjnpzGDPcUr0U6eGQ+T3XLY9slatwZYWL1dA==" - }, - "node_modules/@hotwired/stimulus": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@hotwired/stimulus/-/stimulus-3.2.2.tgz", - "integrity": "sha512-eGeIqNOQpXoPAIP7tC1+1Yc1yl1xnwYqg+3mzqxyrbE5pg5YFBZcA6YoTiByJB6DKAEsiWtl6tjTJS4IYtbB7A==", - "license": "MIT" - }, - "node_modules/@hotwired/turbo": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@hotwired/turbo/-/turbo-8.0.4.tgz", - "integrity": "sha512-mlZEFUZrJnpfj+g/XeCWWuokvQyN68WvM78JM+0jfSFc98wegm259vCbC1zSllcspRwbgXK31ibehCy5PA78/Q==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/@hotwired/turbo-rails": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/@hotwired/turbo-rails/-/turbo-rails-8.0.4.tgz", - "integrity": "sha512-GHCv5+B2VzYZZvMFpg/g9JLx/8pl/8chcubSB7T+Xn1zYOMqAKB6cT80vvWUzxdwfm/2KfaRysfDz+BmvtjFaw==", - "license": "MIT", - "dependencies": { - "@hotwired/turbo": "^8.0.4", - "@rails/actioncable": "^7.0" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@popperjs/core": { - "version": "2.11.8", - "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", - "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/@rails/actioncable": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@rails/actioncable/-/actioncable-7.1.3.tgz", - "integrity": "sha512-ojNvnoZtPN0pYvVFtlO7dyEN9Oml1B6IDM+whGKVak69MMYW99lC2NOWXWeE3bmwEydbP/nn6ERcpfjHVjYQjA==", - "license": "MIT" - }, - "node_modules/@rails/actiontext": { - "version": "7.1.3-4", - "resolved": "https://registry.npmjs.org/@rails/actiontext/-/actiontext-7.1.3-4.tgz", - "integrity": "sha512-Yt0aFwV4vmQDH0SPMKACF5WnZ88vn8KHpCbPJIGnFcj4likev+SkmdCLu/TpcHrFQwHWXt+GwKE924Ny92YXAg==", - "license": "MIT", - "dependencies": { - "@rails/activestorage": ">= 7.1.0-alpha" - }, - "peerDependencies": { - "trix": "^2.0.0" - } - }, - "node_modules/@rails/activestorage": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/@rails/activestorage/-/activestorage-7.1.3.tgz", - "integrity": "sha512-B+RFYAU8vdTPFg0IJcRp2ey0Qw9hpcUOqHHcWqftDJ76ZMBi9+m/UUeMJlNsSd0l9eD+1HLlFSo1X//cY4yiDw==", - "license": "MIT", - "dependencies": { - "spark-md5": "^3.0.1" - } - }, - "node_modules/@rails/request.js": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@rails/request.js/-/request.js-0.0.11.tgz", - "integrity": "sha512-2U3uYS0kbljt+pAstN+LIlZOl7xmOKig5N6FrvtUWO1wq0zR1Hf90fHfD2SYiyV8yH1nyKpoTmbLqWT0xe1zDg==" - }, - "node_modules/@sindresorhus/merge-streams": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", - "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@stimulus-components/lightbox": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@stimulus-components/lightbox/-/lightbox-4.0.0.tgz", - "integrity": "sha512-pj4PfnGANbc3Ef6I/5aPeRS8Tj2rK1b9dn8rlpJZs1K9narqM57ZtUTutHZsnOv95+4LqQVktj+auMgz+LKzjw==", - "dependencies": { - "lightgallery": "^2.7.2" - }, - "peerDependencies": { - "@hotwired/stimulus": "^3.0.0" - } - }, - "node_modules/@vscode/l10n": { - "version": "0.0.18", - "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", - "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==", - "dev": true - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/autoprefixer": { - "version": "10.4.19", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.19.tgz", - "integrity": "sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.0", - "caniuse-lite": "^1.0.30001599", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.0", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true - }, - "node_modules/bootstrap": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", - "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/twbs" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/bootstrap" - } - ], - "license": "MIT", - "peerDependencies": { - "@popperjs/core": "^2.11.8" - } - }, - "node_modules/bootstrap-icons": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/bootstrap-icons/-/bootstrap-icons-1.11.3.tgz", - "integrity": "sha512-+3lpHrCw/it2/7lBL15VR0HEumaBss0+f/Lb6ZvHISn1mlK83jjFpooTLsMWbIjJMDjDjOExMsTxnXSIT4k4ww==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/twbs" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/bootstrap" - } - ], - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.23.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", - "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001640", - "electron-to-chromium": "^1.4.820", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.1.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001642", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz", - "integrity": "sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "license": "MIT" - }, - "node_modules/core-js": { - "version": "3.39.0", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.39.0.tgz", - "integrity": "sha512-raM0ew0/jJUqkJ0E6e8UDtl+y/7ktFivgWvqw8dNSQeNWoSDLvQ1H/RN3aPXB9tBd4/FhyR4RDPGhsNIMsAn7g==", - "dev": true, - "hasInstallScript": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true, - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/dependency-graph": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-0.11.0.tgz", - "integrity": "sha512-JeMq7fEshyepOWDfcfHK06N3MhyPhz++vtqWhMT5O9A3K42rdsEDpfdVqjaqaAhsw6a+ZqeDvQVtD0hFHQWrzg==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "dev": true, - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ] - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", - "dev": true, - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.4.827", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.827.tgz", - "integrity": "sha512-VY+J0e4SFcNfQy19MEoMdaIcZLmDCprqvBtkii1WTCTQHpRvf5N8+3kTYCgL/PcntvwQvmMJWTuDPsq+IlhWKQ==", - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "dev": true, - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/esbuild": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", - "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.0", - "@esbuild/android-arm": "0.23.0", - "@esbuild/android-arm64": "0.23.0", - "@esbuild/android-x64": "0.23.0", - "@esbuild/darwin-arm64": "0.23.0", - "@esbuild/darwin-x64": "0.23.0", - "@esbuild/freebsd-arm64": "0.23.0", - "@esbuild/freebsd-x64": "0.23.0", - "@esbuild/linux-arm": "0.23.0", - "@esbuild/linux-arm64": "0.23.0", - "@esbuild/linux-ia32": "0.23.0", - "@esbuild/linux-loong64": "0.23.0", - "@esbuild/linux-mips64el": "0.23.0", - "@esbuild/linux-ppc64": "0.23.0", - "@esbuild/linux-riscv64": "0.23.0", - "@esbuild/linux-s390x": "0.23.0", - "@esbuild/linux-x64": "0.23.0", - "@esbuild/netbsd-x64": "0.23.0", - "@esbuild/openbsd-arm64": "0.23.0", - "@esbuild/openbsd-x64": "0.23.0", - "@esbuild/sunos-x64": "0.23.0", - "@esbuild/win32-arm64": "0.23.0", - "@esbuild/win32-ia32": "0.23.0", - "@esbuild/win32-x64": "0.23.0" - } - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-glob": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", - "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fastq": { - "version": "1.17.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", - "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-stdin": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-9.0.0.tgz", - "integrity": "sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globby": { - "version": "14.0.2", - "resolved": "https://registry.npmjs.org/globby/-/globby-14.0.2.tgz", - "integrity": "sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==", - "license": "MIT", - "dependencies": { - "@sindresorhus/merge-streams": "^2.1.0", - "fast-glob": "^3.3.2", - "ignore": "^5.2.4", - "path-type": "^5.0.0", - "slash": "^5.1.0", - "unicorn-magic": "^0.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC" - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "bin": { - "he": "bin/he" - } - }, - "node_modules/ignore": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", - "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "license": "ISC" - }, - "node_modules/immutable": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.6.tgz", - "integrity": "sha512-Ju0+lEMyzMVZarkTn/gqRpdqd5dOPaz1mCZ0SH3JV6iFw81PldE/PEB1hWVEA288HPt4WXW8O7AWxB10M+03QQ==", - "license": "MIT" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "dev": true - }, - "node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/lightgallery": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/lightgallery/-/lightgallery-2.8.1.tgz", - "integrity": "sha512-K9bRsrKQM4UyjNUXOX+mW1IMWKvWbDmK42x5nNzgxsi5CNEYMfLoRsA27F1lXp+yegeqdXusymS9w16Fd8a3Rg==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.7", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", - "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/node-html-parser": { - "version": "6.1.13", - "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-6.1.13.tgz", - "integrity": "sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==", - "dev": true, - "dependencies": { - "css-select": "^5.1.0", - "he": "1.2.0" - } - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "license": "MIT" - }, - "node_modules/nodemon": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.4.tgz", - "integrity": "sha512-wjPBbFhtpJwmIeY2yP7QF+UKzPfltVGtfce1g/bB15/8vCGZj8uxD62b/b9M9/WVgme0NZudpownKN+c0plXlQ==", - "license": "MIT", - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^4", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^7.5.3", - "simple-update-notifier": "^2.0.0", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/path-type": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-5.0.0.tgz", - "integrity": "sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-cli": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/postcss-cli/-/postcss-cli-11.0.0.tgz", - "integrity": "sha512-xMITAI7M0u1yolVcXJ9XTZiO9aO49mcoKQy6pCDFdMh9kGqhzLVpWxeD/32M/QBmkhcGypZFFOLNLmIW4Pg4RA==", - "license": "MIT", - "dependencies": { - "chokidar": "^3.3.0", - "dependency-graph": "^0.11.0", - "fs-extra": "^11.0.0", - "get-stdin": "^9.0.0", - "globby": "^14.0.0", - "picocolors": "^1.0.0", - "postcss-load-config": "^5.0.0", - "postcss-reporter": "^7.0.0", - "pretty-hrtime": "^1.0.3", - "read-cache": "^1.0.0", - "slash": "^5.0.0", - "yargs": "^17.0.0" - }, - "bin": { - "postcss": "index.js" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-load-config": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-5.1.0.tgz", - "integrity": "sha512-G5AJ+IX0aD0dygOE0yFZQ/huFFMSNneyfp0e3/bT05a8OfPC5FUoZRPfGijUdGOJNMewJiwzcHJXFafFzeKFVA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.1.1", - "yaml": "^2.4.2" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "jiti": ">=1.21.0", - "postcss": ">=8.0.9", - "tsx": "^4.8.1" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - }, - "postcss": { - "optional": true - }, - "tsx": { - "optional": true - } - } - }, - "node_modules/postcss-reporter": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-reporter/-/postcss-reporter-7.1.0.tgz", - "integrity": "sha512-/eoEylGWyy6/DOiMP5lmFRdmDKThqgn7D6hP2dXKJI/0rJSO1ADFNngZfDzxL0YAxFvws+Rtpuji1YIHj4mySA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "picocolors": "^1.0.0", - "thenby": "^1.3.4" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT" - }, - "node_modules/pretty-hrtime": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", - "integrity": "sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "license": "MIT" - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "dev": true - }, - "node_modules/request-light": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.7.0.tgz", - "integrity": "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==", - "dev": true - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/sass": { - "version": "1.77.8", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.77.8.tgz", - "integrity": "sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==", - "license": "MIT", - "dependencies": { - "chokidar": ">=3.0.0 <4.0.0", - "immutable": "^4.0.0", - "source-map-js": ">=0.6.2 <2.0.0" - }, - "bin": { - "sass": "sass.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/simple-update-notifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", - "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/slash": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", - "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/sortablejs": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.3.tgz", - "integrity": "sha512-zdK3/kwwAK1cJgy1rwl1YtNTbRmc8qW/+vgXf75A7NHag5of4pyI6uK86ktmQETyWRH7IGaE73uZOOBcGxgqZg==" - }, - "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/spark-md5": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/spark-md5/-/spark-md5-3.0.2.tgz", - "integrity": "sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw==", - "license": "(WTFPL OR MIT)" - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tabby-agent": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/tabby-agent/-/tabby-agent-1.7.0.tgz", - "integrity": "sha512-jA8cCO39V+nOvFZP3GaXlxQEqqwXJvvz9tlgVFcqbq2gcWTBSUV4SbxoPYDRKIhY27oTXfeG0SHkAaoL+km5GA==", - "dev": true, - "bin": { - "tabby-agent": "dist/node/index.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/thenby": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/thenby/-/thenby-1.3.4.tgz", - "integrity": "sha512-89Gi5raiWA3QZ4b2ePcEwswC3me9JIg+ToSgtE0JWeCynLnLxNr/f9G+xfo9K+Oj4AFdom8YNJjibIARTJmapQ==", - "license": "Apache-2.0" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/touch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", - "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", - "license": "ISC", - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, - "node_modules/trix": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/trix/-/trix-2.1.3.tgz", - "integrity": "sha512-LqMp67LiKMQytAHKqNL1Jgmfz69ViW+WBOQTPA2BlMIuxic1mw5vHgDtOE0bvvojUdjAxh0EJtLpJn6BC/2JKw==", - "license": "MIT" - }, - "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", - "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "license": "MIT" - }, - "node_modules/unicorn-magic": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", - "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/universalify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", - "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/vscode-css-languageserver-bin": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vscode-css-languageserver-bin/-/vscode-css-languageserver-bin-1.4.0.tgz", - "integrity": "sha512-KWrF5f4RYYe8RBDfqb1c0Sdf9xPS2Ly/Z/T18H+uUOMw2QyzIrkxv4bMKy5GFfPm4479k6Ln4ji4UHqSmhGf3g==", - "dev": true, - "dependencies": { - "vscode-css-languageservice": "^3.0.9-next.18", - "vscode-languageserver": "^4.1.3", - "vscode-languageserver-protocol-foldingprovider": "^2.0.1" - }, - "bin": { - "css-languageserver": "cssServerMain.js" - }, - "engines": { - "node": "*" - } - }, - "node_modules/vscode-css-languageservice": { - "version": "3.0.13", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-3.0.13.tgz", - "integrity": "sha512-RWkO/c/A7iXhHEy3OuEqkCqavDjpD4NF2Ca8vjai+ZtEYNeHrm1ybTnBYLP4Ft1uXvvaaVtYA9HrDjD6+CUONg==", - "dev": true, - "dependencies": { - "vscode-languageserver-types": "^3.13.0", - "vscode-nls": "^4.0.0" - } - }, - "node_modules/vscode-html-languageservice": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.3.1.tgz", - "integrity": "sha512-ysUh4hFeW/WOWz/TO9gm08xigiSsV/FOAZ+DolgJfeLftna54YdmZ4A+lIn46RbdO3/Qv5QHTn1ZGqmrXQhZyA==", - "dev": true, - "dependencies": { - "@vscode/l10n": "^0.0.18", - "vscode-languageserver-textdocument": "^1.0.12", - "vscode-languageserver-types": "^3.17.5", - "vscode-uri": "^3.0.8" - } - }, - "node_modules/vscode-json-languageservice": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.4.1.tgz", - "integrity": "sha512-5czFGNyVPxz3ZJYl8R3a3SuIj5gjhmGF4Wv05MRPvD4DEnHK6b8km4VbNMJNHBlTCh7A0aHzUbPVzo+0C18mCA==", - "dev": true, - "dependencies": { - "@vscode/l10n": "^0.0.18", - "jsonc-parser": "^3.3.1", - "vscode-languageserver-textdocument": "^1.0.12", - "vscode-languageserver-types": "^3.17.5", - "vscode-uri": "^3.0.8" - } - }, - "node_modules/vscode-jsonrpc": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", - "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/vscode-langservers-extracted": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/vscode-langservers-extracted/-/vscode-langservers-extracted-4.10.0.tgz", - "integrity": "sha512-EFf9uQI4dAKbzMQFjDvVm1xJq1DXAQvBEuEfPGrK/xzfsL5xWTfIuRr90NgfmqwO+IEt6vLZm9EOj6R66xIifg==", - "dev": true, - "dependencies": { - "@vscode/l10n": "^0.0.18", - "core-js": "^3.20.1", - "jsonc-parser": "^3.2.1", - "regenerator-runtime": "^0.13.9", - "request-light": "^0.7.0", - "semver": "^7.6.1", - "typescript": "^4.0.5", - "vscode-css-languageservice": "^6.2.14", - "vscode-html-languageservice": "^5.2.0", - "vscode-json-languageservice": "^5.3.11", - "vscode-languageserver": "^10.0.0-next.3", - "vscode-languageserver-textdocument": "^1.0.11", - "vscode-languageserver-types": "^3.17.5", - "vscode-markdown-languageservice": "^0.5.0-alpha.6", - "vscode-nls": "^5.2.0", - "vscode-uri": "^3.0.8" - }, - "bin": { - "vscode-css-language-server": "bin/vscode-css-language-server", - "vscode-eslint-language-server": "bin/vscode-eslint-language-server", - "vscode-html-language-server": "bin/vscode-html-language-server", - "vscode-json-language-server": "bin/vscode-json-language-server", - "vscode-markdown-language-server": "bin/vscode-markdown-language-server" - } - }, - "node_modules/vscode-langservers-extracted/node_modules/vscode-css-languageservice": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.1.tgz", - "integrity": "sha512-1BzTBuJfwMc3A0uX4JBdJgoxp74cjj4q2mDJdp49yD/GuAq4X0k5WtK6fNcMYr+FfJ9nqgR6lpfCSZDkARJ5qQ==", - "dev": true, - "dependencies": { - "@vscode/l10n": "^0.0.18", - "vscode-languageserver-textdocument": "^1.0.12", - "vscode-languageserver-types": "3.17.5", - "vscode-uri": "^3.0.8" - } - }, - "node_modules/vscode-langservers-extracted/node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.6.tgz", - "integrity": "sha512-KCSvUNsFiVciG9iqjJKBZOd66CN3ZKohDlYRmoOi+pd8l15MFLZ8wRG4c+wuzePGba/8WcCG2TM+C/GVlvuaeA==", - "dev": true, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/vscode-langservers-extracted/node_modules/vscode-languageserver": { - "version": "10.0.0-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.11.tgz", - "integrity": "sha512-cmobSrVDYhlh/t02vz/bV8nNpds8mus5HnILULae2iAvOjoaJPnTAp0jJWoYdUqTpIVzT9JV6JMKqLEvdqpeqg==", - "dev": true, - "dependencies": { - "vscode-languageserver-protocol": "3.17.6-next.11" - }, - "bin": { - "installServerIntoExtension": "bin/installServerIntoExtension" - } - }, - "node_modules/vscode-langservers-extracted/node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.11.tgz", - "integrity": "sha512-GeJxEp1TiLsp79f8WG5n10wLViXfgFKb99hU9K8m7KDWM95/QFEqWkm79f9LVm54tUK74I91a9EeiQLCS/FABQ==", - "dev": true, - "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" - } - }, - "node_modules/vscode-langservers-extracted/node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.5.tgz", - "integrity": "sha512-QFmf3Yl1tCgUQfA77N9Me/LXldJXkIVypQbty2rJ1DNHQkC+iwvm4Z2tXg9czSwlhvv0pD4pbF5mT7WhAglolw==", - "dev": true - }, - "node_modules/vscode-langservers-extracted/node_modules/vscode-nls": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-5.2.0.tgz", - "integrity": "sha512-RAaHx7B14ZU04EU31pT+rKz2/zSl7xMsfIZuo8pd+KZO6PXtQmpevpq3vxvWNcrGbdmhM/rr5Uw5Mz+NBfhVng==", - "dev": true - }, - "node_modules/vscode-languageserver": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-4.4.2.tgz", - "integrity": "sha512-61y8Raevi9EigDgg9NelvT9cUAohiEbUl1LOwQQgOCAaNX62yKny/ddi0uC+FUTm4CzsjhBu+06R+vYgfCYReA==", - "dev": true, - "dependencies": { - "vscode-languageserver-protocol": "^3.10.3", - "vscode-uri": "^1.0.5" - }, - "bin": { - "installServerIntoExtension": "bin/installServerIntoExtension" - } - }, - "node_modules/vscode-languageserver-protocol": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", - "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", - "dev": true, - "dependencies": { - "vscode-jsonrpc": "8.2.0", - "vscode-languageserver-types": "3.17.5" - } - }, - "node_modules/vscode-languageserver-protocol-foldingprovider": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol-foldingprovider/-/vscode-languageserver-protocol-foldingprovider-2.0.1.tgz", - "integrity": "sha512-N8bOS8i0xuQMn/y0bijyefDbOsMl6hiH6LDREYWavTLTM5jbj44EiQfStsbmAv/0eaFKkL/jf5hW7nWwBy2HBw==", - "dev": true, - "dependencies": { - "vscode-languageserver-protocol": "^3.7.2", - "vscode-languageserver-types": "^3.7.2" - } - }, - "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", - "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", - "dev": true - }, - "node_modules/vscode-languageserver-types": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", - "dev": true - }, - "node_modules/vscode-languageserver/node_modules/vscode-uri": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-1.0.8.tgz", - "integrity": "sha512-obtSWTlbJ+a+TFRYGaUumtVwb+InIUVI0Lu0VBUAPmj2cU5JutEXg3xUE0c2J5Tcy7h2DEKVJBFi+Y9ZSFzzPQ==", - "dev": true - }, - "node_modules/vscode-markdown-languageservice": { - "version": "0.5.0-alpha.8", - "resolved": "https://registry.npmjs.org/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.5.0-alpha.8.tgz", - "integrity": "sha512-b2NgVMZvzI/7hRL32Kcu9neAAPFQzkcf/Fqwlxbz9p1/Q7aIorGACOGGo00s72AJtwjkCJ29eVJwUlFMFbPKqA==", - "dev": true, - "dependencies": { - "@vscode/l10n": "^0.0.10", - "node-html-parser": "^6.1.5", - "picomatch": "^2.3.1", - "vscode-languageserver-protocol": "^3.17.1", - "vscode-languageserver-textdocument": "^1.0.11", - "vscode-uri": "^3.0.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/vscode-markdown-languageservice/node_modules/@vscode/l10n": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.10.tgz", - "integrity": "sha512-E1OCmDcDWa0Ya7vtSjp/XfHFGqYJfh+YPC1RkATU71fTac+j1JjCcB3qwSzmlKAighx2WxhLlfhS0RwAN++PFQ==", - "dev": true - }, - "node_modules/vscode-nls": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/vscode-nls/-/vscode-nls-4.1.2.tgz", - "integrity": "sha512-7bOHxPsfyuCqmP+hZXscLhiHwe7CSuFE4hyhbs22xPIhQ4jv99FcR4eBzfYYVLP356HNFpdvz63FFb/xw6T4Iw==", - "dev": true - }, - "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", - "dev": true - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yaml": { - "version": "2.4.5", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", - "integrity": "sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - } - } -} diff --git a/package.json b/package.json index 38a1f98..c32d279 100644 --- a/package.json +++ b/package.json @@ -35,4 +35,4 @@ "vscode-css-languageserver-bin": "^1.4.0", "vscode-langservers-extracted": "^4.10.0" } -} +} \ No newline at end of file diff --git a/test/controllers/projects_controller_test.rb b/test/controllers/projects_controller_test.rb new file mode 100644 index 0000000..c2514b6 --- /dev/null +++ b/test/controllers/projects_controller_test.rb @@ -0,0 +1,55 @@ +require "test_helper" + +class ProjectsControllerTest < ::ControllerTest + teardown do + logout + end + + setup do + @project = projects(:one) + User.create!(email_address: "test@example.com", password: "password") + login("test@example.com", "password") + end + + test "should get index" do + get projects_url + assert_response :success + end + + test "should get new" do + get new_project_url + assert_response :success + end + + test "should create project" do + assert_difference("Project.count") do + post projects_url, params: { project: { name: @project.name } } + end + + assert_redirected_to project_url(Project.last) + end + + test "should show project" do + get project_url(@project) + assert_response :success + end + + test "should get edit" do + get edit_project_url(@project) + assert_response :success + end + + test "should update project" do + patch project_url(@project), params: { project: { name: @project.name } } + assert_redirected_to project_url(@project) + end + + test "should destroy project" do + p = Project.create!(name: "empty") + assert_difference("Project.count", -1) do + delete project_url(p) + end + + assert_redirected_to projects_url + end +end diff --git a/test/controllers/reports_controller_test.rb b/test/controllers/reports_controller_test.rb index f46759f..0b9f9e7 100644 --- a/test/controllers/reports_controller_test.rb +++ b/test/controllers/reports_controller_test.rb @@ -14,18 +14,18 @@ class ReportsControllerTest < ::ControllerTest end test "should get index" do - get reports_url + get project_reports_url(@report.project) assert_response :success end test "should get new" do - get new_report_url + get new_project_report_url(@report.project) assert_response :success end test "should create report" do assert_difference("Report.count") do - post reports_url, params: { report: { comment: @report.comment, name: @report.name } } + post project_reports_url(@report.project), params: { report: { comment: @report.comment, name: @report.name } } end assert_redirected_to report_url(Report.last) @@ -51,6 +51,6 @@ class ReportsControllerTest < ::ControllerTest delete report_url(@report) end - assert_redirected_to reports_url + assert_redirected_to project_url(@report.project) end end diff --git a/test/fixtures/projects.yml b/test/fixtures/projects.yml new file mode 100644 index 0000000..7d41224 --- /dev/null +++ b/test/fixtures/projects.yml @@ -0,0 +1,7 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + name: MyString + +two: + name: MyString diff --git a/test/fixtures/reports.yml b/test/fixtures/reports.yml index 7d41224..169701e 100644 --- a/test/fixtures/reports.yml +++ b/test/fixtures/reports.yml @@ -2,6 +2,8 @@ one: name: MyString + project: one two: name: MyString + project: one diff --git a/test/models/project_test.rb b/test/models/project_test.rb new file mode 100644 index 0000000..5df4ca4 --- /dev/null +++ b/test/models/project_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class ProjectTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/system/projects_test.rb b/test/system/projects_test.rb new file mode 100644 index 0000000..2134947 --- /dev/null +++ b/test/system/projects_test.rb @@ -0,0 +1,47 @@ +require "application_system_test_case" + +class ProjectsTest < ApplicationSystemTestCase + setup do + @project = projects(:one) + login_test + end + + teardown do + logout + end + + test "visiting the index" do + visit projects_url + assert_selector "h1", text: "Projekte" + end + + test "should create project" do + visit projects_url + click_on "Projekt hinzufügen" + + fill_in "Name", with: @project.name + click_on "Projekt erstellen" + + assert_text "Project was successfully created" + click_on "Projekte Liste" + end + + test "should update Project" do + visit project_url(@project) + click_on "Projekt bearbeiten", match: :first + + fill_in "Name", with: @project.name + click_on "Projekt aktualisieren" + + assert_text "Project was successfully updated" + click_on "Projekte Liste" + end + + test "should destroy Project" do + empty = Project.create!(name: "empty") + visit project_url(empty) + click_on "Projekt löschen", match: :first + + assert_text "Project was successfully destroyed" + end +end diff --git a/test/system/reports_test.rb b/test/system/reports_test.rb index 066b7f7..c7a5ec3 100644 --- a/test/system/reports_test.rb +++ b/test/system/reports_test.rb @@ -13,12 +13,12 @@ class ReportsTest < ApplicationSystemTestCase end test "visiting the index" do - visit reports_url + visit project_reports_url(@report.project) assert_selector "h1", text: "Prüfberichte" end test "should create report" do - visit reports_url + visit project_reports_url(@report.project) click_on "Prüfbericht hinzufügen" fill_in_rich_text_area "Projektbeschreibung", with: @report.comment From 12ea9ea1fb18f23bb5c926f5b054cc15e86d5e7a Mon Sep 17 00:00:00 2001 From: david Date: Sun, 24 Nov 2024 22:37:05 +0100 Subject: [PATCH 42/46] Add missing routes update --- config/routes.rb | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index fd5e5e1..7b3bcee 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -11,24 +11,26 @@ Rails.application.routes.draw do resources :checks resources :link_categories resources :links - resources :reports, shallow: true, except: %i[show] do - resources :pages do - resources :elements do - resources :success_criteria do - collection do - get "from_checklist", action: :new_from_checklist, as: :new_from_checklist - post "from_checklist", action: :create_from_checklist, as: :create_from_checklist - end + resources :projects, shallow: true do + resources :reports, except: [:show] do + resources :pages do + resources :elements do + resources :success_criteria do + collection do + get "from_checklist", action: :new_from_checklist, as: :new_from_checklist + post "from_checklist", action: :create_from_checklist, as: :create_from_checklist + end - member do - get "edit_comment" + member do + get "edit_comment" + end end end end - end - resource :export, only: %i[show] - member do - get "(-/:page_id)", action: :show, as: "", constraints: { id: /\d+/ } + resource :export, only: %i[show] + member do + get "(-/:page_id)", action: :show, as: "", constraints: { id: /\d+/ } + end end end From 297138b0d40ef7a8b467e2ffff4391bb28dfd166 Mon Sep 17 00:00:00 2001 From: david Date: Sun, 24 Nov 2024 22:38:51 +0100 Subject: [PATCH 43/46] add translation --- config/locales/activerecord.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/config/locales/activerecord.yml b/config/locales/activerecord.yml index 2e7311a..7277847 100644 --- a/config/locales/activerecord.yml +++ b/config/locales/activerecord.yml @@ -14,7 +14,6 @@ de-CH: quick_criterion: Quick Kriterium quick_fail: Quick Fail quick_fix: Quick Fix - title: Titel test_instructions: Testanleitung check: id: ID @@ -92,9 +91,6 @@ de-CH: check: one: Check other: Checks - check: - one: Check - other: Checks checklist_entry: one: Zuweisung other: Zuweisungen @@ -122,3 +118,6 @@ de-CH: page: one: Pfad other: Pfade + project: + one: Projekt + other: Projekte From b756df38a9de85c492aca6382dd16845e7af1055 Mon Sep 17 00:00:00 2001 From: david Date: Mon, 25 Nov 2024 00:06:53 +0100 Subject: [PATCH 44/46] Docker config changes for cli editor --- .dockerignore | 2 + .gitignore | 2 + Dockerfile | 235 +++++++-------------------------------------- Procfile.dev | 4 +- bin/dev_entrypoint | 8 ++ docker-compose.yml | 16 ++- 6 files changed, 64 insertions(+), 203 deletions(-) create mode 100755 bin/dev_entrypoint diff --git a/.dockerignore b/.dockerignore index 36170e5..f53753f 100644 --- a/.dockerignore +++ b/.dockerignore @@ -41,3 +41,5 @@ /.forgejo/ /core* + +/.devenv diff --git a/.gitignore b/.gitignore index 1298bd2..f77ab27 100644 --- a/.gitignore +++ b/.gitignore @@ -74,3 +74,5 @@ public/504.html public/505.html public/507.html public/510.html + +/.devenv diff --git a/Dockerfile b/Dockerfile index c502e7d..f588cdd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,20 @@ +ARG RUBY_VERSION=3.2.5 + +FROM ruby:${RUBY_VERSION} + +LABEL maintainer='david@hohl.cloud' + ARG NAME=app ARG UID=1000 ARG GID=1000 ARG APP_PORT=3000 ARG INSTALL_DIR=/${NAME} -ARG RUBY_VERSION=3.2.5 -FROM ruby:${RUBY_VERSION} AS development - ARG NAME ARG UID ARG GID ARG APP_PORT ARG INSTALL_DIR -LABEL maintainer='david@hohl.cloud' - WORKDIR ${INSTALL_DIR} ENV GEM_HOME=${INSTALL_DIR}/.bundle @@ -24,9 +25,7 @@ ENV \ RAILS_ENV=development \ TZ=Europe/Zurich \ PATH=${INSTALL_DIR}/bin:$GEM_HOME/bin:$GEM_HOME/gems/bin:$PATH \ - EDITOR=vim \ - RUBY_DEBUG_DAP_SHOW_PROTOCOL=1 \ - RUBY_DEBUG_OPEN=true + EDITOR=vim RUN \ ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \ @@ -51,212 +50,50 @@ RUN \ apt-get install -yqq --no-install-recommends \ sqlite3 nodejs npm sassc yarn libvips fish ranger pandoc libjemalloc2 && \ apt-get clean && \ - npm i -g tabby-agent \ - vscode-langservers-extracted \ + npm i -g vscode-langservers-extracted \ dockerfile-language-server-nodejs \ @microsoft/compose-language-service \ - yaml-language-server@next && \ + yaml-language-server@next \ + bash-language-server \ + typescript-language-server typescript && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \ truncate -s 0 /var/log/*log && \ gem update --system && \ - bundle config set app_config ${GEM_HOME} + bundle config set app_config ${GEM_HOME} && \ + mkdir -p /home/${NAME}/.ssh && \ + echo "|1|MY0IaoqveusfUdYlTFzGnB95mhI=|XyUGTfOoKc7vGP5WTlFwT1RU+5E= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOLCA3O6oc0+BplWjB5Vi4jPNnGAYo24oANcylCc9i8W\ + |1|pYFp+C6PAszoVmLOZliWYonpzC0=|QbeRMKsmnFBrqoH38UKkjZdISJI= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC2PqML1VEXRdMc/WTjjN00FWw2vDl11q+0glBLQwQiBV5r3kL1kXWP7rUtuBPfivz4/ZpzkpHhWbKbnrLazpvSRHqwgMm6S1mQoH4Gb3wLwCqvoE/M8BSJsXvlJem63y/W4mtQEboZ+YyBzW1Yss41jBW9tfQxXCVlikPWm/O20hr3uealymIH5GaONE65mY9JfPr7z6AAJZ8bAfO69HWp+Q1dRD2sin3/cY+a25eQ6PWmR7g/Xw3/6pNikOj2LMErZdyCxVhCKmHeDDrWzn8qJgfPRqz9Pneb3qIBOw6kJnTfQGc+7KLZmmqRUChoLuw5jTYthngZVXJ/A0XJG30mqtrQj9uD7vqmTFXZl8wNuv52ELOwPrvJIpEFvjjNGbg8iRkjElHKub8eGvQhy/sH96Xv6WqxfGiXvyUTWrBOdhhL102fA+nXcsx90Ln0/Rjg5v5meFCT1a+3SLgQeQ44c9xasDwGFrwlR1x4uP8249SldIhKFO+1oXnskTN8Prs=\ +|1|FisNvukDUhT0l2SVq4qFF1WFoBQ=|rBWKChHJabb1lM0Og5zcH3lxrOQ= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPknmThDfyVupOSMcGdjP14VEX6e5rXrI3ny+FnLjaCNyGYt7hKyRTfvl32UVXFZqvp+gp6BVdqaKI6ey5Mygfs=" >> /home/${NAME}/.ssh/known_hosts ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 + +RUN curl -fsSL https://github.com/tamasfe/taplo/releases/latest/download/taplo-full-linux-x86_64.gz \ + | gzip -d - | install -m 755 /dev/stdin /usr/local/bin/taplo + +# RUN curl -fsSL https://github.com/Feel-ix-343/markdown-oxide/releases/download/v0.24.3/markdown-oxide-v0.24.3-x86_64-unknown-linux-gnu | install -m 755 /dev/stdin /usr/local/bin/markdown-oxide + +RUN curl -fsSL https://github.com/artempyanykh/marksman/releases/download/2024-11-20/marksman-linux-x64 | install -m 755 /dev/stdin /usr/local/bin/marksman + ARG HELIX_VERSION=24.07 RUN curl -L https://github.com/helix-editor/helix/releases/download/${HELIX_VERSION}/helix-${HELIX_VERSION}-x86_64-linux.tar.xz -o /tmp/helix.tar.xz && \ cd /opt/ && \ tar -xf /tmp/helix.tar.xz && \ - ln -s /opt/helix-${HELIX_VERSION}-x86_64-linux/hx /usr/local/bin chmod +x /usr/local/bin/ + ln -s /opt/helix-${HELIX_VERSION}-x86_64-linux/hx /usr/local/bin -RUN mkdir -p ~/.config/fish ~/.config/helix +RUN mkdir -p /home/app/.config/{helix, fish} -COPY <> .build_version - -USER ${NAME} - -ENTRYPOINT [ "bin/entrypoint" ] - -CMD [ "rails", "server", "--binding", "0.0.0.0", "--no-daemon", "--port" , "3000" ] +ENTRYPOINT ["/usr/local/bin/dev_entrypoint"] +CMD ["/bin/sh", "-c", "dev"] diff --git a/Procfile.dev b/Procfile.dev index 66fe22e..5c7ab2e 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,3 +1,3 @@ -web: env RUBY_DEBUG_OPEN=true bin/rails server -b 0 -js: yarn build --watch +web: RUBY_DEBUG_OPEN=true bin/rails server -b 0 +js: yarn build --watch css: yarn watch:css diff --git a/bin/dev_entrypoint b/bin/dev_entrypoint new file mode 100755 index 0000000..3062b49 --- /dev/null +++ b/bin/dev_entrypoint @@ -0,0 +1,8 @@ +#!/bin/bash + +if [ -f /app/tmp/pids/server.pid ]; then + rm /app/tmp/pids/server.pid +fi + +exec "$@" + diff --git a/docker-compose.yml b/docker-compose.yml index dc06931..5ce4a06 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,12 +9,14 @@ services: app: &app build: context: . - target: development + restart: "unless-stopped" + command: ["/bin/sh", "-c", "/app/bin/dev"] volumes: - ./:/app:cached - ${PWD}:${PWD} - - ${HOME}/.tabby-client:/home/app/.tabby-client - ${SSH_AUTH_SOCK}:/ssh-agent + - .devenv/helix:/home/app/.config/helix + - .devenv/fish:/home/app/.config/fish env_file: - .env environment: @@ -37,6 +39,16 @@ services: - traefik - default + edit: + <<: *app + restart: "no" + labels: [] + depends_on: [] + command: ["hx", "."] + entrypoint: null + networks: + - default + chrome: image: selenium/standalone-chrome shm_size: 2g From 4c31dbbed061a89906d6ab7ff6ecd51e0e5f7177 Mon Sep 17 00:00:00 2001 From: david Date: Fri, 16 May 2025 18:01:29 +0200 Subject: [PATCH 45/46] Update docker --- .ruby-version | 2 +- Dockerfile | 136 ++++++++++----- Gemfile | 3 +- Gemfile.lock | 383 ++++++++++++++++++++++-------------------- bin/build | 70 ++++++++ bin/docker-entrypoint | 18 +- compose.yml | 59 +++++++ 7 files changed, 441 insertions(+), 230 deletions(-) create mode 100755 bin/build create mode 100644 compose.yml diff --git a/.ruby-version b/.ruby-version index f13c6f4..0f9e134 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -ruby-3.3.5 +ruby-3.4 diff --git a/Dockerfile b/Dockerfile index f588cdd..4573018 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,18 +1,17 @@ -ARG RUBY_VERSION=3.2.5 - -FROM ruby:${RUBY_VERSION} - -LABEL maintainer='david@hohl.cloud' +ARG RUBY_VERSION=3.4 ARG NAME=app ARG UID=1000 ARG GID=1000 -ARG APP_PORT=3000 -ARG INSTALL_DIR=/${NAME} +ARG INSTALL_DIR=/home/${NAME}/src + +FROM ruby:${RUBY_VERSION} as development + +LABEL maintainer='david@hohl.cloud' + ARG NAME ARG UID ARG GID -ARG APP_PORT ARG INSTALL_DIR WORKDIR ${INSTALL_DIR} @@ -40,6 +39,7 @@ RUN \ --disabled-login \ --shell /bin/bash \ ${NAME} && \ + chown -R ${NAME}:${NAME} /home/${NAME} && \ apt-get update && \ apt-get install -yqq --no-install-recommends \ gnupg2 \ @@ -48,52 +48,100 @@ RUN \ echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ apt-get update -yqq && \ apt-get install -yqq --no-install-recommends \ - sqlite3 nodejs npm sassc yarn libvips fish ranger pandoc libjemalloc2 && \ + sqlite3 nodejs npm sassc yarn libvips libjemalloc2 && \ apt-get clean && \ - npm i -g vscode-langservers-extracted \ - dockerfile-language-server-nodejs \ - @microsoft/compose-language-service \ - yaml-language-server@next \ - bash-language-server \ - typescript-language-server typescript && \ rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \ truncate -s 0 /var/log/*log && \ gem update --system && \ - bundle config set app_config ${GEM_HOME} && \ - mkdir -p /home/${NAME}/.ssh && \ - echo "|1|MY0IaoqveusfUdYlTFzGnB95mhI=|XyUGTfOoKc7vGP5WTlFwT1RU+5E= ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOLCA3O6oc0+BplWjB5Vi4jPNnGAYo24oANcylCc9i8W\ - |1|pYFp+C6PAszoVmLOZliWYonpzC0=|QbeRMKsmnFBrqoH38UKkjZdISJI= ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC2PqML1VEXRdMc/WTjjN00FWw2vDl11q+0glBLQwQiBV5r3kL1kXWP7rUtuBPfivz4/ZpzkpHhWbKbnrLazpvSRHqwgMm6S1mQoH4Gb3wLwCqvoE/M8BSJsXvlJem63y/W4mtQEboZ+YyBzW1Yss41jBW9tfQxXCVlikPWm/O20hr3uealymIH5GaONE65mY9JfPr7z6AAJZ8bAfO69HWp+Q1dRD2sin3/cY+a25eQ6PWmR7g/Xw3/6pNikOj2LMErZdyCxVhCKmHeDDrWzn8qJgfPRqz9Pneb3qIBOw6kJnTfQGc+7KLZmmqRUChoLuw5jTYthngZVXJ/A0XJG30mqtrQj9uD7vqmTFXZl8wNuv52ELOwPrvJIpEFvjjNGbg8iRkjElHKub8eGvQhy/sH96Xv6WqxfGiXvyUTWrBOdhhL102fA+nXcsx90Ln0/Rjg5v5meFCT1a+3SLgQeQ44c9xasDwGFrwlR1x4uP8249SldIhKFO+1oXnskTN8Prs=\ -|1|FisNvukDUhT0l2SVq4qFF1WFoBQ=|rBWKChHJabb1lM0Og5zcH3lxrOQ= ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBPknmThDfyVupOSMcGdjP14VEX6e5rXrI3ny+FnLjaCNyGYt7hKyRTfvl32UVXFZqvp+gp6BVdqaKI6ey5Mygfs=" >> /home/${NAME}/.ssh/known_hosts + bundle config set app_config ${GEM_HOME} ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 -RUN curl -fsSL https://github.com/tamasfe/taplo/releases/latest/download/taplo-full-linux-x86_64.gz \ - | gzip -d - | install -m 755 /dev/stdin /usr/local/bin/taplo - -# RUN curl -fsSL https://github.com/Feel-ix-343/markdown-oxide/releases/download/v0.24.3/markdown-oxide-v0.24.3-x86_64-unknown-linux-gnu | install -m 755 /dev/stdin /usr/local/bin/markdown-oxide - -RUN curl -fsSL https://github.com/artempyanykh/marksman/releases/download/2024-11-20/marksman-linux-x64 | install -m 755 /dev/stdin /usr/local/bin/marksman - -ARG HELIX_VERSION=24.07 -RUN curl -L https://github.com/helix-editor/helix/releases/download/${HELIX_VERSION}/helix-${HELIX_VERSION}-x86_64-linux.tar.xz -o /tmp/helix.tar.xz && \ - cd /opt/ && \ - tar -xf /tmp/helix.tar.xz && \ - ln -s /opt/helix-${HELIX_VERSION}-x86_64-linux/hx /usr/local/bin - -RUN mkdir -p /home/app/.config/{helix, fish} - -RUN mkdir -p /home/app/.config/helix/runtime/queries/slim && \ - curl https://raw.githubusercontent.com/kolen/tree-sitter-slim/refs/heads/master/queries/highlights.scm -o /home/app/.config/helix/runtime/queries/slim/highlights.scm && \ - curl https://raw.githubusercontent.com/kolen/tree-sitter-slim/refs/heads/master/queries/injections.scm -o /home/app/.config/helix/runtime/queries/slim/injections.scm && \ - chown 1000:1000 -R /home/app - -RUN mkdir -p /opt/helix-${HELIX_VERSION}-x86_64-linux/runtime/queries/slim && \ - curl https://raw.githubusercontent.com/kolen/tree-sitter-slim/refs/heads/master/queries/highlights.scm -o /opt/helix-${HELIX_VERSION}-x86_64-linux/runtime/queries/slim/highlights.scm && \ - curl https://raw.githubusercontent.com/kolen/tree-sitter-slim/refs/heads/master/queries/injections.scm -o /opt/helix-${HELIX_VERSION}-x86_64-linux/runtime/queries/slim/injections.scm - COPY bin/dev_entrypoint /usr/local/bin/dev_entrypoint USER ${NAME} ENTRYPOINT ["/usr/local/bin/dev_entrypoint"] + CMD ["/bin/sh", "-c", "dev"] + +FROM development AS build + +USER root + +# Wir wollen die bundles nicht im app directory +ENV GEM_HOME=/usr/local/bundle \ + BUNDLE_WITHOUT="development test" \ + RAILS_ENV=production \ + SECRET_KEY_BASE_DUMMY=1 + +ENV PATH=${INSTALL_DIR}/bin:$GEM_HOME/bin:$GEM_HOME/gems/bin:$PATH + +COPY ./Gemfile ./Gemfile.lock ./ + + +RUN bundle config set app_config ${GEM_HOME} && \ + bundle config && \ + bundle install && \ + rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \ + bundle exec bootsnap precompile --gemfile + +RUN bundle config +COPY . . + +RUN bundle exec bootsnap precompile app/ lib/ && \ + rails assets:precompile && \ + rm -rf log/* tmp/* && \ + chown -R ${NAME}:${NAME} log tmp + + +FROM ruby:${RUBY_VERSION}-slim AS release + +ARG UID +ARG GID +ARG NAME=app +ARG INSTALL_DIR +ARG BUILD_DIR=${INSTALL_DIR} +ARG INSTALL_DIR=/app + +ENV GEM_HOME=/usr/local/bundle \ + BUNDLE_WITHOUT="development test" \ + BUNDLE_SYSTEM=1 \ + RAILS_ENV=production \ + LANG=C.UTF-8 \ + INSTALL_DIR=${INSTALL_DIR} \ + TZ=Europe/Zurich + +ENV PATH=${INSTALL_DIR}/bin:$GEM_HOME/bin:$GEM_HOME/gems/bin:$PATH + +WORKDIR ${INSTALL_DIR} + +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && \ + echo $TZ > /etc/timezone && \ + adduser --disabled-password --disabled-login ${NAME} &&\ + apt-get update -yqq && \ + apt-get install -yqq --no-install-recommends \ + sqlite3 libjemalloc2 && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* && \ + gem update --system && \ + truncate -s 0 /var/log/*log + +ENV LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.2 + +RUN pwd + +COPY --from=build ${GEM_HOME} ${GEM_HOME} +COPY --from=build ${BUILD_DIR} . + +RUN ls -la && chown -R ${NAME}:${NAME} ./tmp ./log ./storage + +EXPOSE 3000 + +ENTRYPOINT ["bin/docker-entrypoint"] + +USER ${NAME} + +CMD ["./bin/rails", "server", "-b", "0"] + + diff --git a/Gemfile b/Gemfile index 7268a2b..67d1a34 100644 --- a/Gemfile +++ b/Gemfile @@ -2,8 +2,6 @@ source "https://rubygems.org" -ruby "3.2.5" - # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" gem "rails", "~> 8.0" @@ -50,6 +48,7 @@ gem "bootsnap", require: false gem "bootstrap_form" gem "caxlsx" gem "caxlsx_rails" +gem "deepl-rb", require: "deepl" gem "image_processing", "~> 1.2" gem "openxml-docx" gem "pagy", "~> 9.0" diff --git a/Gemfile.lock b/Gemfile.lock index c9d597d..bf0065f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,29 +1,29 @@ GEM remote: https://rubygems.org/ specs: - actioncable (8.0.0) - actionpack (= 8.0.0) - activesupport (= 8.0.0) + actioncable (8.0.2) + actionpack (= 8.0.2) + activesupport (= 8.0.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (8.0.0) - actionpack (= 8.0.0) - activejob (= 8.0.0) - activerecord (= 8.0.0) - activestorage (= 8.0.0) - activesupport (= 8.0.0) + actionmailbox (8.0.2) + actionpack (= 8.0.2) + activejob (= 8.0.2) + activerecord (= 8.0.2) + activestorage (= 8.0.2) + activesupport (= 8.0.2) mail (>= 2.8.0) - actionmailer (8.0.0) - actionpack (= 8.0.0) - actionview (= 8.0.0) - activejob (= 8.0.0) - activesupport (= 8.0.0) + actionmailer (8.0.2) + actionpack (= 8.0.2) + actionview (= 8.0.2) + activejob (= 8.0.2) + activesupport (= 8.0.2) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (8.0.0) - actionview (= 8.0.0) - activesupport (= 8.0.0) + actionpack (8.0.2) + actionview (= 8.0.2) + activesupport (= 8.0.2) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) @@ -31,35 +31,35 @@ GEM rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) useragent (~> 0.16) - actiontext (8.0.0) - actionpack (= 8.0.0) - activerecord (= 8.0.0) - activestorage (= 8.0.0) - activesupport (= 8.0.0) + actiontext (8.0.2) + actionpack (= 8.0.2) + activerecord (= 8.0.2) + activestorage (= 8.0.2) + activesupport (= 8.0.2) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (8.0.0) - activesupport (= 8.0.0) + actionview (8.0.2) + activesupport (= 8.0.2) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - activejob (8.0.0) - activesupport (= 8.0.0) + activejob (8.0.2) + activesupport (= 8.0.2) globalid (>= 0.3.6) - activemodel (8.0.0) - activesupport (= 8.0.0) - activerecord (8.0.0) - activemodel (= 8.0.0) - activesupport (= 8.0.0) + activemodel (8.0.2) + activesupport (= 8.0.2) + activerecord (8.0.2) + activemodel (= 8.0.2) + activesupport (= 8.0.2) timeout (>= 0.4.0) - activestorage (8.0.0) - actionpack (= 8.0.0) - activejob (= 8.0.0) - activerecord (= 8.0.0) - activesupport (= 8.0.0) + activestorage (8.0.2) + actionpack (= 8.0.2) + activejob (= 8.0.2) + activerecord (= 8.0.2) + activesupport (= 8.0.2) marcel (~> 1.0) - activesupport (8.0.0) + activesupport (8.0.2) base64 benchmark (>= 0.3) bigdecimal @@ -74,18 +74,18 @@ GEM uri (>= 0.13.1) addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) - ast (2.4.2) + ast (2.4.3) base64 (0.2.0) bcrypt (3.1.20) benchmark (0.4.0) - bigdecimal (3.1.8) + bigdecimal (3.1.9) bindex (0.8.1) - bootsnap (1.18.3) + bootsnap (1.18.6) msgpack (~> 1.2) bootstrap_form (5.4.0) actionpack (>= 6.1) activemodel (>= 6.1) - brakeman (6.1.2) + brakeman (7.0.2) racc builder (3.3.0) capybara (3.40.0) @@ -97,7 +97,7 @@ GEM rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - caxlsx (4.1.0) + caxlsx (4.2.0) htmlentities (~> 4.3, >= 4.3.4) marcel (~> 1.0) nokogiri (~> 1.10, >= 1.10.4) @@ -105,25 +105,28 @@ GEM caxlsx_rails (0.6.4) actionpack (>= 3.1) caxlsx (>= 3.0) - concurrent-ruby (1.3.3) - connection_pool (2.4.1) + concurrent-ruby (1.3.5) + connection_pool (2.5.3) crass (1.0.6) - cssbundling-rails (1.4.0) + cssbundling-rails (1.4.3) railties (>= 6.0.0) - date (3.4.0) - debug (1.9.2) + date (3.4.1) + debug (1.10.0) irb (~> 1.10) reline (>= 0.3.8) + deepl-rb (3.2.0) drb (2.2.1) - erubi (1.13.0) + erubi (1.13.1) et-orbi (1.2.11) tzinfo - ffi (1.17.0-aarch64-linux-gnu) - ffi (1.17.0-arm-linux-gnu) - ffi (1.17.0-arm64-darwin) - ffi (1.17.0-x86-linux-gnu) - ffi (1.17.0-x86_64-darwin) - ffi (1.17.0-x86_64-linux-gnu) + ffi (1.17.2-aarch64-linux-gnu) + ffi (1.17.2-aarch64-linux-musl) + ffi (1.17.2-arm-linux-gnu) + ffi (1.17.2-arm-linux-musl) + ffi (1.17.2-arm64-darwin) + ffi (1.17.2-x86_64-darwin) + ffi (1.17.2-x86_64-linux-gnu) + ffi (1.17.2-x86_64-linux-musl) fugit (1.11.1) et-orbi (~> 1, >= 1.2.11) raabro (~> 1.4) @@ -133,24 +136,26 @@ GEM html2slim (0.2.0) hpricot htmlentities (4.3.4) - i18n (1.14.5) + i18n (1.14.7) concurrent-ruby (~> 1.0) - image_processing (1.12.2) - mini_magick (>= 4.9.5, < 5) + image_processing (1.14.0) + mini_magick (>= 4.9.5, < 6) ruby-vips (>= 2.0.17, < 3) - io-console (0.7.2) - irb (1.14.0) + io-console (0.8.0) + irb (1.15.2) + pp (>= 0.6.0) rdoc (>= 4.0.0) reline (>= 0.4.2) - jbuilder (2.12.0) + jbuilder (2.13.0) actionview (>= 5.0.0) activesupport (>= 5.0.0) - jsbundling-rails (1.3.0) + jsbundling-rails (1.3.1) railties (>= 6.0.0) - json (2.7.2) - language_server-protocol (3.17.0.3) - logger (1.6.1) - loofah (2.22.0) + json (2.12.0) + language_server-protocol (3.17.0.5) + lint_roller (1.1.0) + logger (1.7.0) + loofah (2.24.1) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.8.1) @@ -160,31 +165,37 @@ GEM net-smtp marcel (1.0.4) matrix (0.4.2) - mini_magick (4.13.2) + mini_magick (5.2.0) + benchmark + logger mini_mime (1.1.5) - minitest (5.24.1) - msgpack (1.7.2) - net-imap (0.5.0) + minitest (5.25.5) + msgpack (1.8.0) + net-imap (0.5.8) date net-protocol net-pop (0.1.2) net-protocol net-protocol (0.2.2) timeout - net-smtp (0.5.0) + net-smtp (0.5.1) net-protocol - nio4r (2.7.3) - nokogiri (1.16.6-aarch64-linux) + nio4r (2.7.4) + nokogiri (1.18.8-aarch64-linux-gnu) racc (~> 1.4) - nokogiri (1.16.6-arm-linux) + nokogiri (1.18.8-aarch64-linux-musl) racc (~> 1.4) - nokogiri (1.16.6-arm64-darwin) + nokogiri (1.18.8-arm-linux-gnu) racc (~> 1.4) - nokogiri (1.16.6-x86-linux) + nokogiri (1.18.8-arm-linux-musl) racc (~> 1.4) - nokogiri (1.16.6-x86_64-darwin) + nokogiri (1.18.8-arm64-darwin) racc (~> 1.4) - nokogiri (1.16.6-x86_64-linux) + nokogiri (1.18.8-x86_64-darwin) + racc (~> 1.4) + nokogiri (1.18.8-x86_64-linux-gnu) + racc (~> 1.4) + nokogiri (1.18.8-x86_64-linux-musl) racc (~> 1.4) openxml-docx (0.11.5) nokogiri @@ -194,77 +205,83 @@ GEM openxml-drawingml (0.3.1) nokogiri openxml-package (~> 0.3.2) - openxml-package (0.3.4) + openxml-package (0.3.5) nokogiri ox - rubyzip - ox (2.14.18) - pagy (9.0.2) + rubyzip (~> 2.3) + ox (2.14.22) + bigdecimal (>= 3.0) + pagy (9.3.4) pandoc-ruby (2.1.10) - parallel (1.25.1) - parser (3.3.4.0) + parallel (1.27.0) + parser (3.3.8.0) ast (~> 2.4.1) racc pdf-core (0.10.0) + pp (0.6.2) + prettyprint prawn (2.5.0) matrix (~> 0.4) pdf-core (~> 0.10.0) ttfunk (~> 1.8) - prawn-markup (1.0.0) + prawn-markup (1.1.0) nokogiri prawn prawn-table - prawn-rails (1.4.2) + prawn-rails (1.6.0) actionview (>= 3.1.0) + activesupport (>= 3.1.0) prawn prawn-table prawn-table (0.2.2) prawn (>= 1.3.0, < 3.0.0) - prism (1.2.0) + prettyprint (0.2.0) + prism (1.4.0) propshaft (1.1.0) actionpack (>= 7.0.0) activesupport (>= 7.0.0) rack railties (>= 7.0.0) - psych (5.1.2) + psych (5.2.6) + date stringio - public_suffix (6.0.0) - puma (6.4.2) + public_suffix (6.0.2) + puma (6.6.0) nio4r (~> 2.0) raabro (1.4.0) - racc (1.8.0) - rack (3.1.7) - rack-session (2.0.0) + racc (1.8.1) + rack (3.1.14) + rack-session (2.1.1) + base64 (>= 0.1.0) rack (>= 3.0.0) - rack-test (2.1.0) + rack-test (2.2.0) rack (>= 1.3) - rackup (2.1.0) + rackup (2.2.1) rack (>= 3) - webrick (~> 1.8) - rails (8.0.0) - actioncable (= 8.0.0) - actionmailbox (= 8.0.0) - actionmailer (= 8.0.0) - actionpack (= 8.0.0) - actiontext (= 8.0.0) - actionview (= 8.0.0) - activejob (= 8.0.0) - activemodel (= 8.0.0) - activerecord (= 8.0.0) - activestorage (= 8.0.0) - activesupport (= 8.0.0) + rails (8.0.2) + actioncable (= 8.0.2) + actionmailbox (= 8.0.2) + actionmailer (= 8.0.2) + actionpack (= 8.0.2) + actiontext (= 8.0.2) + actionview (= 8.0.2) + activejob (= 8.0.2) + activemodel (= 8.0.2) + activerecord (= 8.0.2) + activestorage (= 8.0.2) + activesupport (= 8.0.2) bundler (>= 1.15.0) - railties (= 8.0.0) + railties (= 8.0.2) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.0) + rails-html-sanitizer (1.6.2) loofah (~> 2.21) - nokogiri (~> 1.14) - railties (8.0.0) - actionpack (= 8.0.0) - activesupport (= 8.0.0) + nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) + railties (8.0.2) + actionpack (= 8.0.2) + activesupport (= 8.0.2) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) @@ -272,62 +289,62 @@ GEM zeitwerk (~> 2.6) rainbow (3.1.1) rake (13.2.1) - rbs (3.6.1) + rbs (3.9.4) logger - rdoc (6.7.0) + rdoc (6.13.1) psych (>= 4.0.0) - regexp_parser (2.9.2) - reline (0.5.9) + regexp_parser (2.10.0) + reline (0.6.1) io-console (~> 0.5) - rexml (3.3.2) - strscan - rubocop (1.65.0) + rexml (3.4.1) + rubocop (1.75.6) json (~> 2.3) - language_server-protocol (>= 3.17.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) - regexp_parser (>= 2.4, < 3.0) - rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.31.1, < 2.0) + regexp_parser (>= 2.9.3, < 3.0) + rubocop-ast (>= 1.44.0, < 2.0) ruby-progressbar (~> 1.7) - unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.31.3) - parser (>= 3.3.1.0) - rubocop-capybara (2.21.0) - rubocop (~> 1.41) - rubocop-minitest (0.35.1) - rubocop (>= 1.61, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) - rubocop-performance (1.21.1) - rubocop (>= 1.48.1, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rails (2.25.1) + unicode-display_width (>= 2.4.0, < 4.0) + rubocop-ast (1.44.1) + parser (>= 3.3.7.2) + prism (~> 1.4) + rubocop-capybara (2.22.1) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) + rubocop-performance (1.25.0) + lint_roller (~> 1.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.38.0, < 2.0) + rubocop-rails (2.31.0) activesupport (>= 4.2.0) + lint_roller (~> 1.1) rack (>= 1.1) - rubocop (>= 1.33.0, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rails-omakase (1.0.0) - rubocop - rubocop-minitest - rubocop-performance - rubocop-rails - ruby-lsp (0.20.1) + rubocop (>= 1.75.0, < 2.0) + rubocop-ast (>= 1.38.0, < 2.0) + rubocop-rails-omakase (1.1.0) + rubocop (>= 1.72) + rubocop-performance (>= 1.24) + rubocop-rails (>= 2.30) + ruby-lsp (0.23.20) language_server-protocol (~> 3.17.0) prism (>= 1.2, < 2.0) rbs (>= 3, < 4) sorbet-runtime (>= 0.5.10782) - ruby-lsp-rails (0.3.21) - ruby-lsp (>= 0.20.0, < 0.21.0) + ruby-lsp-rails (0.4.3) + ruby-lsp (>= 0.23.18, < 0.24.0) ruby-progressbar (1.13.0) - ruby-vips (2.2.1) + ruby-vips (2.2.3) ffi (~> 1.12) - rubyzip (2.3.2) + logger + rubyzip (2.4.1) sablon (0.4.1) nokogiri (>= 1.8.5) rubyzip (>= 1.3.0) - securerandom (0.3.1) - selenium-webdriver (4.23.0) + securerandom (0.4.1) + selenium-webdriver (4.32.0) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) @@ -336,60 +353,64 @@ GEM slim (5.2.1) temple (~> 0.10.0) tilt (>= 2.1.0) - solid_queue (1.0.1) + solid_queue (1.1.5) activejob (>= 7.1) activerecord (>= 7.1) concurrent-ruby (>= 1.3.1) fugit (~> 1.11.0) railties (>= 7.1) thor (~> 1.3.1) - sorbet-runtime (0.5.11625) - sqlite3 (2.2.0-aarch64-linux-gnu) - sqlite3 (2.2.0-arm-linux-gnu) - sqlite3 (2.2.0-arm64-darwin) - sqlite3 (2.2.0-x86-linux-gnu) - sqlite3 (2.2.0-x86_64-darwin) - sqlite3 (2.2.0-x86_64-linux-gnu) - stimulus-rails (1.3.3) + sorbet-runtime (0.5.12109) + sqlite3 (2.6.0-aarch64-linux-gnu) + sqlite3 (2.6.0-aarch64-linux-musl) + sqlite3 (2.6.0-arm-linux-gnu) + sqlite3 (2.6.0-arm-linux-musl) + sqlite3 (2.6.0-arm64-darwin) + sqlite3 (2.6.0-x86_64-darwin) + sqlite3 (2.6.0-x86_64-linux-gnu) + sqlite3 (2.6.0-x86_64-linux-musl) + stimulus-rails (1.3.4) railties (>= 6.0.0) - stringio (3.1.1) - strscan (3.1.0) + stringio (3.1.7) temple (0.10.3) - thor (1.3.1) - tilt (2.4.0) - timeout (0.4.1) + thor (1.3.2) + tilt (2.6.0) + timeout (0.4.3) ttfunk (1.8.0) bigdecimal (~> 3.1) - turbo-rails (2.0.5) - actionpack (>= 6.0.0) - activejob (>= 6.0.0) - railties (>= 6.0.0) + turbo-rails (2.0.13) + actionpack (>= 7.1.0) + railties (>= 7.1.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - unicode-display_width (2.5.0) - uri (1.0.1) - useragent (0.16.10) + unicode-display_width (3.1.4) + unicode-emoji (~> 4.0, >= 4.0.4) + unicode-emoji (4.0.4) + uri (1.0.3) + useragent (0.16.11) web-console (4.2.1) actionview (>= 6.0.0) activemodel (>= 6.0.0) bindex (>= 0.4.0) railties (>= 6.0.0) - webrick (1.8.1) websocket (1.2.11) - websocket-driver (0.7.6) + websocket-driver (0.7.7) + base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.6.16) + zeitwerk (2.7.2) PLATFORMS - aarch64-linux - arm-linux + aarch64-linux-gnu + aarch64-linux-musl + arm-linux-gnu + arm-linux-musl arm64-darwin - x86-linux x86_64-darwin - x86_64-linux + x86_64-linux-gnu + x86_64-linux-musl DEPENDENCIES bcrypt (~> 3.1.7) @@ -401,6 +422,7 @@ DEPENDENCIES caxlsx_rails cssbundling-rails debug + deepl-rb html2slim image_processing (~> 1.2) jbuilder @@ -429,8 +451,5 @@ DEPENDENCIES tzinfo-data web-console -RUBY VERSION - ruby 3.2.5p208 - BUNDLED WITH - 2.5.15 + 2.6.2 diff --git a/bin/build b/bin/build new file mode 100755 index 0000000..31c1d20 --- /dev/null +++ b/bin/build @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +server=phela + +# Save current stash state +initial_stash_count=$(git stash list | wc -l) + +# Check if working directory is clean +if [[ -n $(git status --porcelain) ]]; then + echo "Repository is dirty. Stashing changes..." + git stash push --include-untracked --quiet + stashed=true +else + echo "Repository is clean." + stashed=false +fi + +if [[ -n "$1" ]]; then + tag="$1" +else + tag=$(git describe --tags --exact-match 2>/dev/null) +fi + +if [[ -n $tag ]]; then + echo "Current Git tag: $tag" +else + branch=$(git rev-parse --abbrev-ref HEAD) + tag=$branch-$(git rev-parse HEAD) + echo "Current commit SHA-1: $tag" +fi + +baseimage=code.hohl.cloud/jwa11y/a11yist +image=$baseimage:$tag + +echo "Building tag $image" +docker build --progress quiet -t $image --target release . + + +# Restore the stash if we stashed +if [ "$stashed" = true ]; then + echo "Restoring stashed changes..." + current_stash_count=$(git stash list | wc -l) + if (( current_stash_count > initial_stash_count )); then + git stash pop --quiet + echo "Changes restored." + else + echo "No stash to restore." + fi +else + echo "No changes to restore." +fi + +echo "Pushing image $image" +docker push --quiet $image + +read -p "Do you want to SSH into $server and run 'deploy'? [y/N] " answer +if [[ "$answer" =~ ^[Yy]$ ]]; then + echo "Connecting to $server and running 'deploy'..." + baseimage_esc=$(echo $baseimage | sed 's/\//\\\//g' | sed 's/\./\\\./g') + tag_esc=$(echo $image | sed 's/\//\\\//g' | sed 's/\./\\\./g') + read -r -d '' remote_script <> deployments.log +REMOTE + echo "Execute deployment" + echo "$remote_script" | ssh "$server" "bash -s" +fi + diff --git a/bin/docker-entrypoint b/bin/docker-entrypoint index 67ef493..f89fe81 100755 --- a/bin/docker-entrypoint +++ b/bin/docker-entrypoint @@ -1,8 +1,24 @@ #!/bin/bash -e +echo "entrypoint docker: ${@} ${ASSETS_PATH} :: ${@: 1:1} / ${@: 2:1}" + # If running the rails server then create or migrate existing database -if [ "${1}" == "./bin/rails" ] && [ "${2}" == "server" ]; then +if [ "${@: 1:1}" == "./bin/rails" ] && [ "${@: 2:1}" == "server" ]; then + if [ -f ${INSTALL_DIR}/pids/server.pid ]; then + rm ${INSTALL_DIR}/pids/server.pid + fi + + echo "Prepare db..." ./bin/rails db:prepare + + echo "Copy assets to <$ASSETS_PATH>?" + + if [[ -n "$ASSETS_PATH" ]]; then + echo "Copy assets to $ASSETS_PATH" + find "$INSTALL_DIR/public" -mindepth 1 -exec echo "copy " {} \; + find "$INSTALL_DIR/public" -mindepth 1 -exec cp -a {} "$ASSETS_PATH" \; + fi fi + exec "${@}" diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..964fd9a --- /dev/null +++ b/compose.yml @@ -0,0 +1,59 @@ +# This is available in $COMPOSE_PROJECT_NAME +name: a11yist + +networks: + ingress: + external: true + +services: + app: &app + build: + context: . + target: development + args: + NAME: ${USER} + UID: 1000 + GID: 1000 + INSTALL_DIR: ${PWD} + volumes: + - ${SSH_AUTH_SOCK}:/ssh-agent + - ${PWD}:${PWD} + env_file: + - .env + environment: + APP_HOST: ${COMPOSE_PROJECT_NAME}.localhost + HISTFILE: /app/tmp/.bash_history + IRBRC: /app/.irbrc + LOG_LEVEL: debug + PSQL_HISTORY: /app/tmp/.psql_history + RAILS_ENV: development + RAILS_SERVE_STATIC_FILES: 1 + SELENIUM_REMOTE_URL: http://chrome:4444/wd/hub + SSH_AUTH_SOCK: /ssh-agent + TRUSTED_IP: 172.16.0.0/12,192.168.0.0/16,10.0.0.0/24 + labels: + caddy: "http://${COMPOSE_PROJECT_NAME}.localhost" + caddy.reverse_proxy: "{{upstreams 3000}}" + networks: + - ingress + - default + + edit: + <<: *app + restart: "no" + labels: [] + depends_on: [] + command: [ "hx", "." ] + entrypoint: null + networks: + - default + + chrome: + image: selenium/standalone-chrome + shm_size: 2g + labels: + caddy: "http://browser.${COMPOSE_PROJECT_NAME}.localhost" + caddy.reverse_proxy: "{{upstreams 7900}}" + networks: + - ingress + - default From 4dd445be575cc4af228eb148b98537028e575212 Mon Sep 17 00:00:00 2001 From: david Date: Fri, 16 May 2025 19:02:33 +0200 Subject: [PATCH 46/46] wip: wcag structure --- Procfile.dev | 2 +- README.md | 4 + app/controllers/application_controller.rb | 7 +- app/controllers/checks_controller.rb | 2 +- app/controllers/concerns/backoffice_menu.rb | 1 + app/controllers/guidelines_controller.rb | 58 +++++++++++++ .../controllers/toast_controller.js | 8 +- app/models/check.rb | 22 ++++- app/models/guideline.rb | 18 +++++ app/models/report.rb | 4 + app/views/backoffice/show.html.erb | 8 +- app/views/checks/_check.html.slim | 6 +- app/views/checks/_form.html.slim | 4 +- app/views/elements/_form.html.slim | 3 +- app/views/exports/show.html.slim | 31 +------ app/views/guidelines/_form.html.erb | 7 ++ app/views/guidelines/_guideline.html.erb | 15 ++++ app/views/guidelines/_guideline.json.jbuilder | 2 + app/views/guidelines/edit.html.erb | 8 ++ app/views/guidelines/index.html.erb | 28 +++++++ app/views/guidelines/index.json.jbuilder | 1 + app/views/guidelines/new.html.erb | 7 ++ app/views/guidelines/show.html.erb | 9 +++ app/views/guidelines/show.json.jbuilder | 1 + app/views/home/show.html.slim | 6 ++ app/views/layouts/_toast.html.slim | 2 +- app/views/layouts/application.html.slim | 2 +- app/views/pages/_form.html.slim | 3 +- app/views/success_criteria/_form.html.slim | 1 + bin/dev | 2 +- bin/dev_entrypoint | 4 +- config/credentials.yml.enc | 2 +- config/initializers/deepl.rb | 6 ++ config/routes.rb | 1 + .../20250101163808_create_guidelines.rb | 12 +++ ...250101163835_add_guideline_id_to_checks.rb | 6 ++ ...move_unique_number_constraint_on_checks.rb | 5 ++ db/schema.rb | 20 +++-- db/seeds.rb | 3 +- docker-compose.yml | 62 -------------- lib/tasks/import.rake | 81 ++++++++++++++++--- test/controllers/checks_controller_test.rb | 6 +- .../controllers/guidelines_controller_test.rb | 49 +++++++++++ test/fixtures/checks.yml | 6 +- test/fixtures/guidelines.yml | 11 +++ test/models/guideline_test.rb | 7 ++ test/system/guidelines_test.rb | 45 +++++++++++ tmp/pids/.keep | 0 48 files changed, 461 insertions(+), 137 deletions(-) create mode 100644 app/controllers/guidelines_controller.rb create mode 100644 app/models/guideline.rb create mode 100644 app/views/guidelines/_form.html.erb create mode 100644 app/views/guidelines/_guideline.html.erb create mode 100644 app/views/guidelines/_guideline.json.jbuilder create mode 100644 app/views/guidelines/edit.html.erb create mode 100644 app/views/guidelines/index.html.erb create mode 100644 app/views/guidelines/index.json.jbuilder create mode 100644 app/views/guidelines/new.html.erb create mode 100644 app/views/guidelines/show.html.erb create mode 100644 app/views/guidelines/show.json.jbuilder create mode 100644 config/initializers/deepl.rb create mode 100644 db/migrate/20250101163808_create_guidelines.rb create mode 100644 db/migrate/20250101163835_add_guideline_id_to_checks.rb create mode 100644 db/migrate/20250101171836_remove_unique_number_constraint_on_checks.rb delete mode 100644 docker-compose.yml create mode 100644 test/controllers/guidelines_controller_test.rb create mode 100644 test/fixtures/guidelines.yml create mode 100644 test/models/guideline_test.rb create mode 100644 test/system/guidelines_test.rb delete mode 100644 tmp/pids/.keep diff --git a/Procfile.dev b/Procfile.dev index 5c7ab2e..8cec4c8 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -1,3 +1,3 @@ web: RUBY_DEBUG_OPEN=true bin/rails server -b 0 -js: yarn build --watch +js: yarn build --watch=forever css: yarn watch:css diff --git a/README.md b/README.md index 4f83e3b..ec40b05 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,7 @@ `rails new -n a11yist -d sqlite3 --skip-action-mailbox --css bootstrap --js esbuild .` + +## Installation + +To [install](readme) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7a1bd32..41d97ec 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -25,10 +25,6 @@ class ApplicationController < ActionController::Base label: Project.model_name.human(count: 2), icon: :'folder', path: :projects }, - # { - # label: Report.model_name.human(count: 2), - # icon: :'journal-text', - # path: :reports }, { label: I18n.t("backoffice"), icon: :gear, @@ -36,7 +32,8 @@ class ApplicationController < ActionController::Base active: %w[backoffice checklists checks links link_categories].include?(controller_name) }, { label: "Konto", - path: profile_path + path: profile_path, + icon: "person-circle" } ] else diff --git a/app/controllers/checks_controller.rb b/app/controllers/checks_controller.rb index 303415e..f1c1e3e 100644 --- a/app/controllers/checks_controller.rb +++ b/app/controllers/checks_controller.rb @@ -81,7 +81,7 @@ class ChecksController < ApplicationController # Only allow a list of trusted parameters through. def check_params - params.require(:check).permit(:principle_id, + params.require(:check).permit(:guideline_id, :number, :name_de, :name_en, diff --git a/app/controllers/concerns/backoffice_menu.rb b/app/controllers/concerns/backoffice_menu.rb index e2f72fb..9ab5278 100644 --- a/app/controllers/concerns/backoffice_menu.rb +++ b/app/controllers/concerns/backoffice_menu.rb @@ -5,6 +5,7 @@ module BackofficeMenu [ { label: "Einstellungen", icon: :sliders, path: :backoffice }, { label: Checklist.model_name.human(count: 2), icon: :'list-check', path: :checklists }, + { label: Guideline.model_name.human(count: 2), icon: :'rulers', path: :guidelines }, { label: Check.model_name.human(count: 2), icon: :check2, path: :checks }, { label: Link.model_name.human(count: 2), icon: :link, path: :links }, { label: LinkCategory.model_name.human(count: 2), icon: :folder, path: :link_categories } ] diff --git a/app/controllers/guidelines_controller.rb b/app/controllers/guidelines_controller.rb new file mode 100644 index 0000000..e69449d --- /dev/null +++ b/app/controllers/guidelines_controller.rb @@ -0,0 +1,58 @@ +class GuidelinesController < BackofficeController + before_action :set_guideline, only: %i[ show edit update destroy ] + + # GET /guidelines + def index + @guidelines = Guideline.all + end + + # GET /guidelines/1 + def show + end + + # GET /guidelines/new + def new + @guideline = Guideline.new + end + + # GET /guidelines/1/edit + def edit + end + + # POST /guidelines + def create + @guideline = Guideline.new(guideline_params) + + if @guideline.save + redirect_to @guideline, notice: "Guideline was successfully created." + else + render :new, status: :unprocessable_entity + end + end + + # PATCH/PUT /guidelines/1 + def update + if @guideline.update(guideline_params) + redirect_to @guideline, notice: "Guideline was successfully updated.", status: :see_other + else + render :edit, status: :unprocessable_entity + end + end + + # DELETE /guidelines/1 + def destroy + @guideline.destroy! + redirect_to guidelines_url, notice: "Guideline was successfully destroyed.", status: :see_other + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_guideline + @guideline = Guideline.find(params[:id]) + end + + # Only allow a list of trusted parameters through. + def guideline_params + params.require(:guideline).permit(:principle_id, :number, :name_de, :name_en, :description_de, :description_en) + end +end diff --git a/app/javascript/controllers/toast_controller.js b/app/javascript/controllers/toast_controller.js index 55d8ea1..e8b23e8 100644 --- a/app/javascript/controllers/toast_controller.js +++ b/app/javascript/controllers/toast_controller.js @@ -4,7 +4,11 @@ import * as bootstrap from "bootstrap" // Connects to data-controller="toast" export default class extends Controller { connect() { - const toastBootstrap = bootstrap.Toast.getOrCreateInstance(this.element) - toastBootstrap.show() + const shownKey = `toastsShown[${this.element.getAttribute("data-ts")}]` + if(!window.sessionStorage.getItem(shownKey)) { + window.sessionStorage.setItem(shownKey, Date.now()); + const toastBootstrap = bootstrap.Toast.getOrCreateInstance(this.element) + toastBootstrap.show() + } } } diff --git a/app/models/check.rb b/app/models/check.rb index a201aa8..a60d5e2 100644 --- a/app/models/check.rb +++ b/app/models/check.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Check < ApplicationRecord - belongs_to :principle + belongs_to :guideline has_and_belongs_to_many :links has_and_belongs_to_many :standards @@ -51,9 +51,9 @@ class Check < ApplicationRecord :standard_text, :powerpoint_text - validates :number, uniqueness: true, presence: true + validates :number, uniqueness: { scope: :guideline_id }, presence: true - before_validation { self.number = self.class.maximum(:id).to_i + 1 unless self.number.present? } + # before_validation { self.number = self.class.maximum(:id).to_i + 1 unless self.number.present? } scope(:search, lambda { |terms| # TODO: Search only fields for current locale. @@ -129,4 +129,20 @@ class Check < ApplicationRecord def external_number [ external_number_1, external_number_2, external_number_3 ].compact_blank.join(".") end + + def external_number + [ + guideline.principle.id, + guideline.number, + number + ].compact.join(".") + end + + def full_number + external_number + end + + def to_s + display_label + end end diff --git a/app/models/guideline.rb b/app/models/guideline.rb new file mode 100644 index 0000000..baeac74 --- /dev/null +++ b/app/models/guideline.rb @@ -0,0 +1,18 @@ +class Guideline < ApplicationRecord + belongs_to :principle + + has_many :checks + + has_rich_text :description_de + has_rich_text :description_en + + translates_attributes :name, :description + + def to_s + "#{full_number} #{t_name}" + end + + def full_number + [ principle.id, number ].join(".") + end +end diff --git a/app/models/report.rb b/app/models/report.rb index 68a36ab..df65fc4 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -21,4 +21,8 @@ class Report < ApplicationRecord success_criteria: export_success_criteria } end + + def test + 139 + end end diff --git a/app/views/backoffice/show.html.erb b/app/views/backoffice/show.html.erb index c16d394..8bcbd75 100644 --- a/app/views/backoffice/show.html.erb +++ b/app/views/backoffice/show.html.erb @@ -7,6 +7,12 @@ <%= link_to Checklist.model_name.human(count: Checklist.count), :checklists %>

+

+ +<%= Guideline.count %> +<%= link_to Guideline.model_name.human(count: Guideline.count), :guidelines %> +

+

<%= Check.count %> @@ -27,4 +33,4 @@

  • <%= link_to "ZIP Backup herunterladen", admin_backup_url(format: :zip), class: " ", data: { turbo_prefetch: false, frame: "_top", turbo: false } %>
  • - \ No newline at end of file + diff --git a/app/views/checks/_check.html.slim b/app/views/checks/_check.html.slim index 62996f0..7f99880 100644 --- a/app/views/checks/_check.html.slim +++ b/app/views/checks/_check.html.slim @@ -11,8 +11,8 @@ div id=dom_id(check) th = Check.human_attribute_name(:number) td = check.number tr - th = Principle.model_name.human - td = check.principle&.t_name + th = Guideline.model_name.human + td = check.guideline&.name_en tr th = Standard.model_name.human(count: check.standard_ids.size) td = check.standards.map(&:t_name).sort_by(&:downcase).join(", ") @@ -80,4 +80,4 @@ div id=dom_id(check) ul - check.links.select{ _1.link_category == category }.map { |link| link_to link.text, link.url, target: :_blank }.each do |link| li = link - \ No newline at end of file + diff --git a/app/views/checks/_form.html.slim b/app/views/checks/_form.html.slim index 53e4bb7..9f5bb2c 100644 --- a/app/views/checks/_form.html.slim +++ b/app/views/checks/_form.html.slim @@ -1,9 +1,9 @@ = bootstrap_form_with(model: check, remote: true, data: { controller: "unsaved-changes" }) do |form| h2 Details = multilang_form_field(form, :name) - = form.text_field :number, required: false .row - = form.collection_radio_buttons(:principle_id, Principle.all.sort_by(&:t_name), :id, :t_name) { |b| b.label(class: "col-md-2") { b.radio_button + b.text } } + = form.collection_radio_buttons(:guideline_id, Guideline.all.sort_by(&:full_number), :id, :to_s) { |b| b.label(class: "col-md-2") { b.radio_button + b.text } } + = form.text_field :number, required: false = form.collection_check_boxes :standard_ids, Standard.all.sort_by{ _1.t_name.downcase }, :id, :t_name, include_blank: true h2 Einschränkung/Zugänglichkeit diff --git a/app/views/elements/_form.html.slim b/app/views/elements/_form.html.slim index 4feb80d..b821401 100644 --- a/app/views/elements/_form.html.slim +++ b/app/views/elements/_form.html.slim @@ -6,4 +6,5 @@ - if element.persisted? = safe_display(element.screenshot) { tag.div(link_to(_1.filename.to_s, _1), class: "mb-3") } = form.submit class: "btn btn-primary" - = link_to("Abbrechen", element.persisted? ? element : element.report, class: "btn btn-outline-secondary") + - unless modal? + = link_to("Abbrechen", element.persisted? ? element : element.report, class: "btn btn-outline-secondary") diff --git a/app/views/exports/show.html.slim b/app/views/exports/show.html.slim index d7ca86d..1fe71d1 100644 --- a/app/views/exports/show.html.slim +++ b/app/views/exports/show.html.slim @@ -1,26 +1,3 @@ -/nav - = link_to(@report.name, "##{dom_id(@report)}") - ul - li = link_to("Inhaltsverzeichnis", "#toc") - li - = link_to('Testbericht') - ul - - @report.pages.select { |p| p.elements.any? { |e| e.success_criteria.any?(&:failed?) } }.each do |page| - li - = link_to("#{page.position} #{page.path}", "##{dom_id(page)}") - ul - - page.elements.select { |e| e.success_criteria.any?(&:failed?) }.each do |element| - li - = link_to("#{element.number} #{element.title}") - ul - - element.success_criteria.select(&:failed?).each do |sc| - li = link_to("#{sc.number} #{sc.title}", "##{dom_id(sc)}") - li - = link_to("Anhang") - ul - - @failed_success_criteria.group_by(&:check).each do |check, scs| - li = link_to(check.display_label) - h1.title id=dom_id(@report) = @report.name h2 1 Einschätzung @@ -42,18 +19,18 @@ h2 2 Protokoll - current_page_pos += 1 - current_element_pos = 0 h3 = "2.#{current_page_pos} #{page.path}" + p + strong URL + = page.url - page.elements.select { |e| e.success_criteria.any? { _1.failed? } }.each do |element| - current_element_pos += 1 - current_abs_element_pos += 1 - current_sc_pos = 0 h4 = "2.#{current_page_pos}.#{current_element_pos} #{element.title}" /h3 = "2.#{current_abs_element_pos} #{element.title}" - p - strong Pfad: - span =< page.path = safe_display(element.screenshot) { image_tag(_1.representation(resize_to_fit: [250, 250]))} = element.description - - element.success_criteria.select{ _1.failed? }.each do |sc| + - element.success_criteria.select(&:failed?).each do |sc| - current_sc_pos += 1 /h4 = "2.#{current_abs_element_pos}.#{current_sc_pos} #{sc.title}" diff --git a/app/views/guidelines/_form.html.erb b/app/views/guidelines/_form.html.erb new file mode 100644 index 0000000..d3c952a --- /dev/null +++ b/app/views/guidelines/_form.html.erb @@ -0,0 +1,7 @@ +<%= bootstrap_form_with(model: guideline) do |form| %> + <%= form.collection_select :principle_id, Principle.all.sort_by(&:id), :id, :t_name %> + <%= form.number_field :number %> + <%= form.text_field :name_de %> + <%= form.rich_text_area :description_de %> + <%= form.submit %> +<% end %> diff --git a/app/views/guidelines/_guideline.html.erb b/app/views/guidelines/_guideline.html.erb new file mode 100644 index 0000000..0560533 --- /dev/null +++ b/app/views/guidelines/_guideline.html.erb @@ -0,0 +1,15 @@ +
    +

    + Principle: + <%= guideline.principle.t_name %> +

    + + <%= guideline.description_de %> +

    Richtlinien

    + <% guideline.checks.each do |check| %> +
    +

    <%= link_to check %>

    + <%= check.t_criterion %> +
    + <% end %> +
    diff --git a/app/views/guidelines/_guideline.json.jbuilder b/app/views/guidelines/_guideline.json.jbuilder new file mode 100644 index 0000000..72c091d --- /dev/null +++ b/app/views/guidelines/_guideline.json.jbuilder @@ -0,0 +1,2 @@ +json.extract! guideline, :id, :principle_id, :number, :name_de, :created_at, :updated_at +json.url guideline_url(guideline, format: :json) diff --git a/app/views/guidelines/edit.html.erb b/app/views/guidelines/edit.html.erb new file mode 100644 index 0000000..2411616 --- /dev/null +++ b/app/views/guidelines/edit.html.erb @@ -0,0 +1,8 @@ +

    <%= t("scaffold.pagetitle_edit", model: Guideline.model_name.human) %>

    + +<%= render "form", guideline: @guideline %> + +
    + <%= link_to t("scaffold.link_show", model: Guideline.model_name.human), @guideline %> + <%= link_to t("scaffold.link_index", model: Guideline.model_name.human(count: 2)), guidelines_path %> +
    diff --git a/app/views/guidelines/index.html.erb b/app/views/guidelines/index.html.erb new file mode 100644 index 0000000..fe34cbf --- /dev/null +++ b/app/views/guidelines/index.html.erb @@ -0,0 +1,28 @@ +

    <%= t("scaffold.pagetitle_index", model: Guideline.model_name.human(count: 2)) %>

    + + + + + + + + + + + + <% @guidelines.each do |guideline| %> + + + + + + + + + <% end %> + +
    <%= Guideline.human_attribute_name(:name_de) %><%= Guideline.human_attribute_name(:description_de) %>
    <%= link_to(guideline, url_for(guideline)) %><%= link_to(guideline.description_de, url_for(guideline)) %>
    + +
    + <%= link_to t("scaffold.link_new", model: Guideline.model_name.human), new_guideline_path %> +
    diff --git a/app/views/guidelines/index.json.jbuilder b/app/views/guidelines/index.json.jbuilder new file mode 100644 index 0000000..cd94a2f --- /dev/null +++ b/app/views/guidelines/index.json.jbuilder @@ -0,0 +1 @@ +json.array! @guidelines, partial: "guidelines/guideline", as: :guideline diff --git a/app/views/guidelines/new.html.erb b/app/views/guidelines/new.html.erb new file mode 100644 index 0000000..7e57546 --- /dev/null +++ b/app/views/guidelines/new.html.erb @@ -0,0 +1,7 @@ +

    <%= t("scaffold.pagetitle_new", model: Guideline.model_name.human) %>

    + +<%= render "form", guideline: @guideline %> + +
    + <%= link_to t("scaffold.link_index", model: Guideline.model_name.human(count: 2)), guidelines_path %> +
    diff --git a/app/views/guidelines/show.html.erb b/app/views/guidelines/show.html.erb new file mode 100644 index 0000000..7334fa3 --- /dev/null +++ b/app/views/guidelines/show.html.erb @@ -0,0 +1,9 @@ +

    <%= @guideline %>

    + +<%= render @guideline %> + +
    + <%= link_to t("scaffold.link_edit", model: @guideline.model_name.human), edit_guideline_path(@guideline) %> + <%= link_to t("scaffold.link_index", model: @guideline.model_name.human(count: 2)), guidelines_path %> + <%= button_to t("scaffold.link_destroy", model: @guideline.model_name.human), @guideline, method: :delete, class: "btn btn-outline-danger" %> +
    diff --git a/app/views/guidelines/show.json.jbuilder b/app/views/guidelines/show.json.jbuilder new file mode 100644 index 0000000..a0b1efe --- /dev/null +++ b/app/views/guidelines/show.json.jbuilder @@ -0,0 +1 @@ +json.partial! "guidelines/guideline", guideline: @guideline diff --git a/app/views/home/show.html.slim b/app/views/home/show.html.slim index 1ab4d19..2d5443c 100644 --- a/app/views/home/show.html.slim +++ b/app/views/home/show.html.slim @@ -19,6 +19,12 @@ h1 Dashboard li = link_to(r.name, r) .col-md-6 + h2 Browser Erweiterungen + p = link_to "Language Tools KI Korrektur", "https://languagetool.org/services#browsers" + p = link_to "DeepL Firefox Extension", "https://www.deepl.com/en/firefox-extension" + p = link_to "DeepL Chrome Extension", "https://www.deepl.com/en/chrome-extension" + p = link_to "DeepL Edge Extension", "https://www.deepl.com/en/edge-extension" + h2 Hotkeys p Auf der Bericht-Ausfüllen Seite können folgende Shortcuts verwendet werden: dl diff --git a/app/views/layouts/_toast.html.slim b/app/views/layouts/_toast.html.slim index 5917715..600e969 100644 --- a/app/views/layouts/_toast.html.slim +++ b/app/views/layouts/_toast.html.slim @@ -1,4 +1,4 @@ -.toast class="#{alert ? "text-bg-danger" : "text-bg-info"}" role="alert" aria-live="assertive" aria-atomic="true" data={ controller: "toast" } +.toast class="#{alert ? "text-bg-danger" : "text-bg-info"}" role="alert" aria-live="assertive" aria-atomic="true" data={ controller: "toast", ts: Time.now.to_f } .toast-header /img src="..." class="rounded me-2" alt="..."> /strong.me-auto = heading diff --git a/app/views/layouts/application.html.slim b/app/views/layouts/application.html.slim index ac756a6..a53d602 100644 --- a/app/views/layouts/application.html.slim +++ b/app/views/layouts/application.html.slim @@ -1,5 +1,5 @@ doctype html -html data-bs-theme="#{cookies[:"modeTheme"] || "light"}" data-controller="set-theme" +html lang=:de data-bs-theme="#{cookies[:"modeTheme"] || "light"}" data-controller="set-theme" head title a11ydive meta[name="viewport" content="width=device-width,initial-scale=1"] diff --git a/app/views/pages/_form.html.slim b/app/views/pages/_form.html.slim index 0d0beda..a909d1f 100644 --- a/app/views/pages/_form.html.slim +++ b/app/views/pages/_form.html.slim @@ -2,4 +2,5 @@ = form.text_field :path = form.text_field :url = form.submit - = link_to("Abbrechen", report_path(@page.report), class: "btn btn-outline-secondary") + - unless modal? + = link_to("Abbrechen", report_path(@page.report), class: "btn btn-outline-secondary") diff --git a/app/views/success_criteria/_form.html.slim b/app/views/success_criteria/_form.html.slim index c67c2b0..b459173 100644 --- a/app/views/success_criteria/_form.html.slim +++ b/app/views/success_criteria/_form.html.slim @@ -7,4 +7,5 @@ = form.rich_text_area :test_comment = form.submit class: "btn btn-primary" - unless modal? + p Not MODAL =< link_to "Abbrechen", success_criterion.persisted? ? success_criterion : success_criterion.element, class: "btn btn-outline-secondary" diff --git a/bin/dev b/bin/dev index edcb0e0..eda330c 100755 --- a/bin/dev +++ b/bin/dev @@ -8,4 +8,4 @@ fi # Default to port 3000 if not specified export PORT="${PORT:-3000}" -exec foreman start -f /app/Procfile.dev "$@" +exec foreman start -f Procfile.dev "$@" diff --git a/bin/dev_entrypoint b/bin/dev_entrypoint index 3062b49..1ca3bf7 100755 --- a/bin/dev_entrypoint +++ b/bin/dev_entrypoint @@ -1,7 +1,7 @@ #!/bin/bash -if [ -f /app/tmp/pids/server.pid ]; then - rm /app/tmp/pids/server.pid +if [ -f tmp/pids/server.pid ]; then + rm tmp/pids/server.pid fi exec "$@" diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc index 7741360..d5e9f74 100644 --- a/config/credentials.yml.enc +++ b/config/credentials.yml.enc @@ -1 +1 @@ -vRtWWWCynlFuZljZsCQOV5D6xZlwp/LkuGZ2bPp1IuZiCRfJ2F8CXbubRavkqM0xxpHWkC7hiQ0lSv/dCPtaujG+aJ2Q0+KSw81kJ3xiYd/AiR3ZxO9prk5Zsf0LPLoONGBbYurJIb37hAWQ7h1r1qORLbtQGLb9pYlynObhXvkaYQR5E7Jr0Rcnon0d2uKJ13ukDAHUiIgwCE5/f+Y6UVllTLBCk0a+YDZvkv7xsYNpTTD9bgjpAU/pg6jv1xY0k/gZ8SFRsSQaJs8CYtbhMsyZBvImtguGfc7U7B4cFrgdzUPY473vBKBqm6O0StNagS5muP3/YLLN7xcrRDTBi9n3qllATlfJ9cxZ+JCWZJmyqINI4X+T/sc3lpzlo/grW0W+HwQ/4CZ3LkCN/OOqZlG1HL1u--NhnYNqgGpqSnh7Ut--KGndHl8kyj/uAjRG12R3zg== \ No newline at end of file +TjpgJe/DwNAzOraDbzXUo20hxCLkOn72DlXYkIouG0crCD3m4/LAiwDOlOWwsCO5Je6FzqKZniLUqSfVthChxewvZn+PY7XGTLECbB9gjSpRC6hERUxKIirLot+CH7lkMlM3f4o3NPf2I4vs8j6hooXcc8Vd8l1uMHOU1RHd+8EfPmTetqd0IETnEUdXigL50yjcbpxy8jHGdaeA8hHU3F+jwk7gRv11uVgEfn069qD9tc5AlAFWdnYJj/ZadAX2+bimHyne5Y12gmRAiqu95KXzUs0OlI+Vx9lpFyMQNacuKPPx0AeeBafjnMBozXOSU7MMCBUGzig07Kg+/tJSiekeLX0X2VEj3Ecqe9nL54fAGVSqJmwT19KvjeS4WfjvFDff9KAY4H+vKzfkBQzMFOTJjnMTNHa+dtNly0kUphwDtsFWDhF3PaZQIRCeI7RIXKhFMgrRFE6UL4AFFzhocQ1DAwaZElXFJaKjiCWWCY+acLCRz2AnriR08KMsEGN54nMtcGcWnUS2ewr/txLQDkipjADYmjoxsz+rDqp2IH+4y4ZFJf8=--jX6TlsuL0nwliAuN--lnbA1H3K7bwrmIjgJzAT9A== \ No newline at end of file diff --git a/config/initializers/deepl.rb b/config/initializers/deepl.rb new file mode 100644 index 0000000..007121a --- /dev/null +++ b/config/initializers/deepl.rb @@ -0,0 +1,6 @@ +if Rails.application.credentials[:deepl_api_key] + DeepL.configure do |config| + config.auth_key = Rails.application.credentials[:deepl_api_key] + config.host = "https://api-free.deepl.com" + end +end diff --git a/config/routes.rb b/config/routes.rb index 7b3bcee..1c19c13 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,6 @@ # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html Rails.application.routes.draw do + resources :guidelines resource :session resources :passwords, param: :token namespace :admin do diff --git a/db/migrate/20250101163808_create_guidelines.rb b/db/migrate/20250101163808_create_guidelines.rb new file mode 100644 index 0000000..197cb17 --- /dev/null +++ b/db/migrate/20250101163808_create_guidelines.rb @@ -0,0 +1,12 @@ +class CreateGuidelines < ActiveRecord::Migration[8.0] + def change + create_table :guidelines do |t| + t.references :principle, null: false, foreign_key: true + t.integer :number + t.string :name_de + t.string :name_en + + t.timestamps + end + end +end diff --git a/db/migrate/20250101163835_add_guideline_id_to_checks.rb b/db/migrate/20250101163835_add_guideline_id_to_checks.rb new file mode 100644 index 0000000..09662e8 --- /dev/null +++ b/db/migrate/20250101163835_add_guideline_id_to_checks.rb @@ -0,0 +1,6 @@ +class AddGuidelineIdToChecks < ActiveRecord::Migration[8.0] + def change + add_reference :checks, :guideline, null: false, foreign_key: true + remove_reference :checks, :principle + end +end diff --git a/db/migrate/20250101171836_remove_unique_number_constraint_on_checks.rb b/db/migrate/20250101171836_remove_unique_number_constraint_on_checks.rb new file mode 100644 index 0000000..1406d2b --- /dev/null +++ b/db/migrate/20250101171836_remove_unique_number_constraint_on_checks.rb @@ -0,0 +1,5 @@ +class RemoveUniqueNumberConstraintOnChecks < ActiveRecord::Migration[8.0] + def change + remove_index :checks, :number + end +end diff --git a/db/schema.rb b/db/schema.rb index 754674b..55b94e9 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[8.0].define(version: 2024_11_24_183406) do +ActiveRecord::Schema[8.0].define(version: 2025_01_01_171836) do create_table "account_remember_keys", force: :cascade do |t| t.string "key", null: false t.datetime "deadline", null: false @@ -94,7 +94,6 @@ ActiveRecord::Schema[8.0].define(version: 2024_11_24_183406) do t.boolean "cognitive", default: false, null: false t.boolean "applicable_to_web", default: false, null: false t.boolean "applicable_to_app", default: false, null: false - t.integer "principle_id" t.integer "conformity_level" t.integer "priority" t.boolean "manual_test", default: true, null: false @@ -106,8 +105,8 @@ ActiveRecord::Schema[8.0].define(version: 2024_11_24_183406) do t.integer "external_number_1" t.integer "external_number_2" t.integer "external_number_3" - t.index ["number"], name: "index_checks_on_number", unique: true - t.index ["principle_id"], name: "index_checks_on_principle_id" + t.integer "guideline_id", null: false + t.index ["guideline_id"], name: "index_checks_on_guideline_id" end create_table "checks_links", id: false, force: :cascade do |t| @@ -131,6 +130,16 @@ ActiveRecord::Schema[8.0].define(version: 2024_11_24_183406) do t.index ["page_id"], name: "index_elements_on_page_id" end + create_table "guidelines", force: :cascade do |t| + t.integer "principle_id", null: false + t.integer "number" + t.string "name_de" + t.string "name_en" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["principle_id"], name: "index_guidelines_on_principle_id" + end + create_table "link_categories", force: :cascade do |t| t.string "name" t.text "description" @@ -230,8 +239,9 @@ ActiveRecord::Schema[8.0].define(version: 2024_11_24_183406) do add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" add_foreign_key "checklist_entries", "checklists" add_foreign_key "checklist_entries", "checks" - add_foreign_key "checks", "principles" + add_foreign_key "checks", "guidelines" add_foreign_key "elements", "pages" + add_foreign_key "guidelines", "principles" add_foreign_key "links", "link_categories" add_foreign_key "pages", "reports" add_foreign_key "reports", "projects" diff --git a/db/seeds.rb b/db/seeds.rb index b0ddeb2..bf0bfc7 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -72,6 +72,8 @@ Principle.create!(name_de: "Verständlich", name_en: "Understandable") Principle.create!(name_de: "Robust", name_en: "Robust") Principle.create!(name_de: "Sonstige", name_en: "Other") +LinkCategory.create!(name: "Verstehen") +LinkCategory.create!(name: "WCAG Quick Reference") LinkCategory.create!(name: "Tools") LinkCategory.create!(name: "Beispiele") LinkCategory.create!(name: "Artikel") @@ -92,4 +94,3 @@ Link.create!(url: "https://www.a11yproject.com/", link_category: LinkCategory.find_by(name: "Artikel")) User.find_or_initialize_by(email_address: "admin@example.com").update!(password: "password") -User.find_or_initialize_by(email_address: "goran@quiet.ch").update!(password: "password") \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml deleted file mode 100644 index 5ce4a06..0000000 --- a/docker-compose.yml +++ /dev/null @@ -1,62 +0,0 @@ -# This is available in $COMPOSE_PROJECT_NAME -name: a11yist - -networks: - traefik: - external: true - -services: - app: &app - build: - context: . - restart: "unless-stopped" - command: ["/bin/sh", "-c", "/app/bin/dev"] - volumes: - - ./:/app:cached - - ${PWD}:${PWD} - - ${SSH_AUTH_SOCK}:/ssh-agent - - .devenv/helix:/home/app/.config/helix - - .devenv/fish:/home/app/.config/fish - env_file: - - .env - environment: - RAILS_ENV: development - LOG_LEVEL: debug - TRUSTED_IP: 172.16.0.0/12,192.168.0.0/16,10.0.0.0/24 - SSH_AUTH_SOCK: /ssh-agent - RAILS_SERVE_STATIC_FILES: 1 - APP_HOST: ${COMPOSE_PROJECT_NAME}.localhost - HISTFILE: /app/tmp/.bash_history - PSQL_HISTORY: /app/tmp/.psql_history - IRBRC: /app/.irbrc - SELENIUM_REMOTE_URL: http://chrome:4444/wd/hub - labels: - - traefik.http.routers.app-${COMPOSE_PROJECT_NAME}.entrypoints=http - - traefik.http.routers.app-${COMPOSE_PROJECT_NAME}.rule=Host(`${COMPOSE_PROJECT_NAME}.localhost`) - - traefik.http.services.app-${COMPOSE_PROJECT_NAME}.loadbalancer.server.port=3000 - - traefik.docker.network=traefik - networks: - - traefik - - default - - edit: - <<: *app - restart: "no" - labels: [] - depends_on: [] - command: ["hx", "."] - entrypoint: null - networks: - - default - - chrome: - image: selenium/standalone-chrome - shm_size: 2g - labels: - - traefik.http.routers.chrome-${COMPOSE_PROJECT_NAME}.entrypoints=http - - traefik.http.routers.chrome-${COMPOSE_PROJECT_NAME}.rule=Host(`chrome.${COMPOSE_PROJECT_NAME}.localhost`) - - traefik.http.services.chrome-${COMPOSE_PROJECT_NAME}.loadbalancer.server.port=4444 - - traefik.docker.network=traefik - networks: - - traefik - - default diff --git a/lib/tasks/import.rake b/lib/tasks/import.rake index dccb8d4..ef0cc5a 100644 --- a/lib/tasks/import.rake +++ b/lib/tasks/import.rake @@ -1,20 +1,66 @@ # frozen_string_literal: true + URL = "https://outline-rocks.github.io/wcag/translations/WCAG21-de/" WCAG_22_EN_URL ="https://www.w3.org/TR/WCAG22/" +WCAG_22_EN_QREF_URL = "https://www.w3.org/WAI/WCAG22/quickref/" + +def translate(input) + DeepL.translate(input, "EN", "DE") +end def import_wcag22en doc = Nokogiri::HTML5(URI.open(WCAG_22_EN_URL)) - standards = [Standard.find_by(name_de: "WCAG 2.2"), Standard.find_by(name_de: "EN 301 549")] + qrefdoc = Nokogiri::HTML5(URI.open(WCAG_22_EN_QREF_URL)) + quick_criteria = qrefdoc.css(".guidelines section:has(h4)").each_with_object({}) do |node, h| + h[node.css("h4 strong").first.content] = { + quick_criterion_en: node.css(".sc-content .sc-text p").first.content, + link_url: node.css(".understanding a").first.attribute("href").value, + perm_url: "#{WCAG_22_EN_QREF_URL}##{node.css("article").first.attribute("id").value}" + } + rescue + debugger + end + link_category = LinkCategory.find_by(name: "Verstehen") + qr_category = LinkCategory.find_by(name: "WCAG Quick Reference") + # debugger + # raise ActiveRecord::Rollback + + standards = [ Standard.find_by(name_de: "WCAG 2.2"), Standard.find_by(name_de: "EN 301 549") ] doc.css("section.principle").each do |principle_node| _principle_id = principle_node.attributes["id"].value principle_title = principle_node.css("h2").first.content.scan(/([a-zA-Z]+)/) - principle = Principle.find_by!(name_en: principle_title) + principle = Principle.find_or_create_by!(name_en: principle_title) + principle_node.css("section.guideline").each do |guideline_node| + next unless guideline_node.css("h3").first + + puts guideline_node.css("h3").first&.content + puts guideline_node.css_path + + g_title = guideline_node.css("h3") + .first + .content + .scan(/Guideline \d+\.\d+ (.*)/) + .first + .first + g_number = guideline_node.css("h3 bdi") + .first + .content + .scan(/\d+\.(\d+)/) + .first + .first + g_text = guideline_node.css("> p").first.content + + guideline = Guideline.find_or_create_by(principle: principle, number: g_number, name_en: g_title) + guideline.update(description_en: g_text, description_de: translate(g_text), name_de: translate(g_title)) if guideline.description_de.blank? + guideline_node.css("section.guideline").each do |sc| + puts sc.css_path + puts sc.css("h4 bdi") sc_number, sc_title = sc.css("h4") .first .content - .scan(/Success Criterion (\d+\.\d+\.\d+) (.*)/) + .scan(/Success Criterion \d+\.\d+\.(\d+) (.*)/) .first sc_level = sc.css("p.conformance-level").first&.content&.scan(/\(Level (A+)\)/)&.first&.last sc_url = sc.css("a.self-link").first.attr("href") @@ -23,20 +69,35 @@ def import_wcag22en .select { _1.class == Nokogiri::XML::Element } .select { _1.attr("class").nil? || _1.attr("class") == "note" } .reduce("") { |str, node| str + node.to_s } - - check = Check.find_or_initialize_by(external_number: sc_number) + + check = Check.find_or_initialize_by(guideline_id: guideline.id, number: sc_number) new_standards = Set.new(check.standards) new_standards += standards - check.name_de = sc_title + check.name_de = translate sc_title if check.name_de.blank? check.name_en = sc_title - check.principle = principle check.standards = new_standards check.applicable_to_app = check.applicable_to_web = true - check.external_number = sc_number check.external_url = "#{WCAG_22_EN_URL}#{sc_url}" check.conformity_level = sc_level&.to_sym - check.conformity_notice_de = sc_conformity_notice - check.criterion_de = "
    #{full_text}
    ".gsub('href="#', %(href="#{WCAG_22_EN_URL}#)) + check.conformity_notice_de = translate sc_conformity_notice + check.criterion_de = "
    #{translate full_text}
    ".gsub('href="#', %(href="#{WCAG_22_EN_URL}#)) if check.criterion_de.blank? + qref_attrs = quick_criteria["#{principle.id}.#{guideline.number}.#{sc_number}"] + check.quick_criterion_en = qref_attrs&.dig :quick_criterion_en + check.quick_criterion_de = qref_attrs&.dig :quick_criterion_en + + if qref_attrs&.dig(:link_url) + link = Link.find_or_create_by(link_category:, url: qref_attrs[:link_url]) + link.update(text: "#{check.full_number} Verstehen") + check.links << link + end + + if qref_attrs&.dig(:perm_url) + link = Link.find_or_create_by(link_category: qr_category, url: qref_attrs[:perm_url]) + link.update(text: "#{check.full_number} Quick Reference") + check.links << link + end + # check.criterion_details_en = qref_attrs&.dig :criterion_details_en + # check.criterion_details_de = qref_attrs&.dig :criterion_details_en check.save! end end diff --git a/test/controllers/checks_controller_test.rb b/test/controllers/checks_controller_test.rb index f66c842..669e480 100644 --- a/test/controllers/checks_controller_test.rb +++ b/test/controllers/checks_controller_test.rb @@ -8,7 +8,7 @@ class ChecksControllerTest < ::ControllerTest end setup do - @principle = principles(:one) + @guideline = guidelines(:one) @check = checks(:deletable) User.create!(email_address: "test@example.com", password: "password") login("test@example.com", "password") @@ -27,7 +27,7 @@ class ChecksControllerTest < ::ControllerTest test "should create check" do assert_difference("Check.count") do post checks_url, - params: { check: { principle_id: @principle.id, number: Check.maximum(:number) + 1, level: @check.level, name_de: @check.name_de, position: @check.position, + params: { check: { guideline_id: @guideline.id, number: Check.maximum(:number) + 1, level: @check.level, name_de: @check.name_de, position: @check.position, criterion_de: @check.criterion_de } } end @@ -46,7 +46,7 @@ class ChecksControllerTest < ::ControllerTest test "should update check" do patch check_url(@check), - params: { check: { principle_id: @principle.id, level: @check.level, name_de: @check.t_name, position: @check.position, + params: { check: { guideline_id: @guideline.id, level: @check.level, name_de: @check.t_name, position: @check.position, criterion_de: @check.criterion_de } } assert_redirected_to check_url(@check) end diff --git a/test/controllers/guidelines_controller_test.rb b/test/controllers/guidelines_controller_test.rb new file mode 100644 index 0000000..cf37063 --- /dev/null +++ b/test/controllers/guidelines_controller_test.rb @@ -0,0 +1,49 @@ +require "test_helper" + +class GuidelinesControllerTest < ActionDispatch::IntegrationTest + setup do + skip "login" + @guideline = guidelines(:one) + end + + test "should get index" do + get guidelines_url + assert_response :success + end + + test "should get new" do + get new_guideline_url + assert_response :success + end + + test "should create guideline" do + assert_difference("Guideline.count") do + post guidelines_url, params: { guideline: { name_de: @guideline.name_de, number: @guideline.number, principle_id: @guideline.principle_id } } + end + + assert_redirected_to guideline_url(Guideline.last) + end + + test "should show guideline" do + get guideline_url(@guideline) + assert_response :success + end + + test "should get edit" do + get edit_guideline_url(@guideline) + assert_response :success + end + + test "should update guideline" do + patch guideline_url(@guideline), params: { guideline: { name_de: @guideline.name_de, number: @guideline.number, principle_id: @guideline.principle_id } } + assert_redirected_to guideline_url(@guideline) + end + + test "should destroy guideline" do + assert_difference("Guideline.count", -1) do + delete guideline_url(@guideline) + end + + assert_redirected_to guidelines_url + end +end diff --git a/test/fixtures/checks.yml b/test/fixtures/checks.yml index afadaf9..29d2a96 100644 --- a/test/fixtures/checks.yml +++ b/test/fixtures/checks.yml @@ -5,18 +5,18 @@ one: name_de: MyString level: 1 number: 1 - principle: one + guideline: one two: position: MyString name_de: MyString level: 1 number: 2 - principle: one + guideline: one deletable: position: MyString name_de: MyString level: 1 number: 3 - principle: one + guideline: one diff --git a/test/fixtures/guidelines.yml b/test/fixtures/guidelines.yml new file mode 100644 index 0000000..63db8d4 --- /dev/null +++ b/test/fixtures/guidelines.yml @@ -0,0 +1,11 @@ +# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + principle: one + number: 1 + name_de: MyString + +two: + principle: two + number: 1 + name_de: MyString diff --git a/test/models/guideline_test.rb b/test/models/guideline_test.rb new file mode 100644 index 0000000..eb514f4 --- /dev/null +++ b/test/models/guideline_test.rb @@ -0,0 +1,7 @@ +require "test_helper" + +class GuidelineTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/system/guidelines_test.rb b/test/system/guidelines_test.rb new file mode 100644 index 0000000..f108ac9 --- /dev/null +++ b/test/system/guidelines_test.rb @@ -0,0 +1,45 @@ +require "application_system_test_case" + +class GuidelinesTest < ApplicationSystemTestCase + setup do + @guideline = guidelines(:one) + end + + test "visiting the index" do + visit guidelines_url + assert_selector "h1", text: "Guidelines" + end + + test "should create guideline" do + visit guidelines_url + click_on "New guideline" + + fill_in "Name de", with: @guideline.name_de + fill_in "Number", with: @guideline.number + fill_in "Principle", with: @guideline.principle_id + click_on "Create Guideline" + + assert_text "Guideline was successfully created" + click_on "Back" + end + + test "should update Guideline" do + visit guideline_url(@guideline) + click_on "Edit this guideline", match: :first + + fill_in "Name de", with: @guideline.name_de + fill_in "Number", with: @guideline.number + fill_in "Principle", with: @guideline.principle_id + click_on "Update Guideline" + + assert_text "Guideline was successfully updated" + click_on "Back" + end + + test "should destroy Guideline" do + visit guideline_url(@guideline) + click_on "Destroy this guideline", match: :first + + assert_text "Guideline was successfully destroyed" + end +end diff --git a/tmp/pids/.keep b/tmp/pids/.keep deleted file mode 100644 index e69de29..0000000