katalyst-koi 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +23 -0
- data/Upgrade.md +6 -0
- data/app/assets/builds/koi/admin.css +1 -0
- data/app/assets/builds/koi/nav_items.css +1 -0
- data/app/assets/config/koi.js +10 -0
- data/app/assets/images/koi/application/chevron-right.svg +10 -0
- data/app/assets/images/koi/application/glyphicons-halflings-white.png +0 -0
- data/app/assets/images/koi/application/glyphicons-halflings.png +0 -0
- data/app/assets/images/koi/application/icon-collapse-down.png +0 -0
- data/app/assets/images/koi/application/icon-collapse-up.png +0 -0
- data/app/assets/images/koi/application/icon-file-doc.png +0 -0
- data/app/assets/images/koi/application/icon-file-img.png +0 -0
- data/app/assets/images/koi/application/icon-file-pdf.png +0 -0
- data/app/assets/images/koi/application/icon-file-ppt.png +0 -0
- data/app/assets/images/koi/application/icon-file-unknown.png +0 -0
- data/app/assets/images/koi/application/icon-file-xls.png +0 -0
- data/app/assets/images/koi/application/icon-file-zip.png +0 -0
- data/app/assets/images/koi/application/icon-form-date-picker.png +0 -0
- data/app/assets/images/koi/application/icon-form-error.png +0 -0
- data/app/assets/images/koi/application/icon-index-sort-ascending.png +0 -0
- data/app/assets/images/koi/application/icon-index-sort-descending.png +0 -0
- data/app/assets/images/koi/application/icon-index-sort.png +0 -0
- data/app/assets/images/koi/application/icon-index-sortable.png +0 -0
- data/app/assets/images/koi/application/icon-menu-cursor.png +0 -0
- data/app/assets/images/koi/application/icon-overlay-add.png +0 -0
- data/app/assets/images/koi/application/icon-overlay-close.png +0 -0
- data/app/assets/images/koi/application/icon-sortable.png +0 -0
- data/app/assets/images/koi/application/jcrop.gif +0 -0
- data/app/assets/images/koi/application/loading.gif +0 -0
- data/app/assets/images/koi/application/select-arrow.svg +3 -0
- data/app/assets/images/koi/application/select_arrow.png +0 -0
- data/app/assets/images/koi/application/sort-ascending.png +0 -0
- data/app/assets/images/koi/application/sort-descending.png +0 -0
- data/app/assets/javascripts/koi/admin.js +4 -0
- data/app/assets/javascripts/koi/controllers/application.js +11 -0
- data/app/assets/javascripts/koi/controllers/document_field_controller.js +26 -0
- data/app/assets/javascripts/koi/controllers/file_field_controller.js +143 -0
- data/app/assets/javascripts/koi/controllers/flash_controller.js +12 -0
- data/app/assets/javascripts/koi/controllers/form_request_submit_controller.js +11 -0
- data/app/assets/javascripts/koi/controllers/image_field_controller.js +24 -0
- data/app/assets/javascripts/koi/controllers/index.js +6 -0
- data/app/assets/javascripts/koi/controllers/index_actions_controller.js +61 -0
- data/app/assets/javascripts/koi/controllers/keyboard_controller.js +149 -0
- data/app/assets/javascripts/koi/controllers/navigation_controller.js +84 -0
- data/app/assets/javascripts/koi/controllers/navigation_toggle_controller.js +7 -0
- data/app/assets/javascripts/koi/controllers/show_hide_controller.js +25 -0
- data/app/assets/javascripts/koi/controllers/sluggable_controller.js +30 -0
- data/app/assets/javascripts/koi/controllers/webauthn_authentication_controller.js +23 -0
- data/app/assets/javascripts/koi/controllers/webauthn_registration_controller.js +30 -0
- data/app/assets/javascripts/koi/utils/transition.js +220 -0
- data/app/assets/stylesheets/koi/admin.scss +27 -0
- data/app/assets/stylesheets/koi/base/_button.scss +122 -0
- data/app/assets/stylesheets/koi/base/_icon.scss +29 -0
- data/app/assets/stylesheets/koi/base/_index.scss +18 -0
- data/app/assets/stylesheets/koi/base/_input.scss +13 -0
- data/app/assets/stylesheets/koi/base/_link.scss +26 -0
- data/app/assets/stylesheets/koi/base/_list.scss +11 -0
- data/app/assets/stylesheets/koi/base/_typography.scss +160 -0
- data/app/assets/stylesheets/koi/components/_actions-group.scss +7 -0
- data/app/assets/stylesheets/koi/components/_image-field.scss +33 -0
- data/app/assets/stylesheets/koi/components/_index-actions.scss +69 -0
- data/app/assets/stylesheets/koi/components/_index-table.scss +91 -0
- data/app/assets/stylesheets/koi/components/_index.scss +6 -0
- data/app/assets/stylesheets/koi/components/_item-table.scss +33 -0
- data/app/assets/stylesheets/koi/components/_pagy.scss +41 -0
- data/app/assets/stylesheets/koi/layouts/_banner.scss +7 -0
- data/app/assets/stylesheets/koi/layouts/_content.scss +40 -0
- data/app/assets/stylesheets/koi/layouts/_flash.scss +41 -0
- data/app/assets/stylesheets/koi/layouts/_header.scss +62 -0
- data/app/assets/stylesheets/koi/layouts/_index.scss +48 -0
- data/app/assets/stylesheets/koi/layouts/_main.scss +23 -0
- data/app/assets/stylesheets/koi/layouts/_navigation.scss +156 -0
- data/app/assets/stylesheets/koi/layouts/_stack.scss +13 -0
- data/app/assets/stylesheets/koi/pages/_index.scss +1 -0
- data/app/assets/stylesheets/koi/pages/_login.scss +40 -0
- data/app/assets/stylesheets/koi/themes/_content.scss +5 -0
- data/app/assets/stylesheets/koi/themes/_govuk.scss +52 -0
- data/app/assets/stylesheets/koi/themes/_index.scss +5 -0
- data/app/assets/stylesheets/koi/themes/_kpop.scss +5 -0
- data/app/assets/stylesheets/koi/themes/_navigation.scss +5 -0
- data/app/assets/stylesheets/koi/themes/_trix.scss +32 -0
- data/app/assets/stylesheets/koi/utils/_breakpoints.scss +13 -0
- data/app/assets/stylesheets/koi/utils/_hide.scss +11 -0
- data/app/assets/stylesheets/koi/utils/_index.scss +2 -0
- data/app/assets/stylesheets/koi/utils/_typography.scss +24 -0
- data/app/components/koi/header/edit_component.rb +58 -0
- data/app/components/koi/header/index_component.rb +23 -0
- data/app/components/koi/header/new_component.rb +40 -0
- data/app/components/koi/header/show_component.rb +51 -0
- data/app/components/koi/header_component.html.erb +16 -0
- data/app/components/koi/header_component.rb +28 -0
- data/app/components/koi/index_table_component.rb +21 -0
- data/app/controllers/admin/admin_users_controller.rb +88 -0
- data/app/controllers/admin/application_controller.rb +9 -0
- data/app/controllers/admin/caches_controller.rb +11 -0
- data/app/controllers/admin/credentials_controller.rb +64 -0
- data/app/controllers/admin/dashboards_controller.rb +7 -0
- data/app/controllers/admin/sessions_controller.rb +78 -0
- data/app/controllers/admin/url_rewrites_controller.rb +87 -0
- data/app/controllers/concerns/koi/controller/has_admin_users.rb +49 -0
- data/app/controllers/concerns/koi/controller/has_webauthn.rb +45 -0
- data/app/controllers/concerns/koi/controller/is_admin_controller.rb +52 -0
- data/app/helpers/katalyst/content/editor/errors.rb +21 -0
- data/app/helpers/katalyst/navigation/editor/errors.rb +21 -0
- data/app/helpers/koi/application_helper.rb +7 -0
- data/app/helpers/koi/date_helper.rb +36 -0
- data/app/helpers/koi/definition_list_helper.rb +92 -0
- data/app/helpers/koi/index_actions_helper.rb +99 -0
- data/app/jobs/koi/application_job.rb +6 -0
- data/app/mailers/koi/application_mailer.rb +8 -0
- data/app/models/admin/credential.rb +14 -0
- data/app/models/admin/user.rb +51 -0
- data/app/models/application_record.rb +5 -0
- data/app/models/concerns/koi/model/archivable.rb +55 -0
- data/app/models/url_rewrite.rb +25 -0
- data/app/views/admin/admin_users/_admin.html+row.erb +4 -0
- data/app/views/admin/admin_users/_authentication.html.erb +15 -0
- data/app/views/admin/admin_users/_fields.html.erb +4 -0
- data/app/views/admin/admin_users/edit.html.erb +11 -0
- data/app/views/admin/admin_users/index.html.erb +9 -0
- data/app/views/admin/admin_users/new.html.erb +11 -0
- data/app/views/admin/admin_users/show.html.erb +22 -0
- data/app/views/admin/credentials/new.html.erb +14 -0
- data/app/views/admin/dashboards/show.html.erb +1 -0
- data/app/views/admin/sessions/new.html.erb +19 -0
- data/app/views/admin/shared/icons/_close.html.erb +8 -0
- data/app/views/admin/shared/icons/_cross.html.erb +3 -0
- data/app/views/admin/shared/icons/_menu.html.erb +3 -0
- data/app/views/admin/shared/icons/_refresh.html.erb +8 -0
- data/app/views/admin/url_rewrites/_form_fields.html.erb +3 -0
- data/app/views/admin/url_rewrites/_url_rewrite.html+row.erb +7 -0
- data/app/views/admin/url_rewrites/edit.html.erb +12 -0
- data/app/views/admin/url_rewrites/index.html.erb +10 -0
- data/app/views/admin/url_rewrites/new.html.erb +11 -0
- data/app/views/admin/url_rewrites/show.html.erb +16 -0
- data/app/views/katalyst/content/asides/_aside.html+form.erb +18 -0
- data/app/views/katalyst/content/columns/_column.html+form.erb +18 -0
- data/app/views/katalyst/content/contents/_content.html+form.erb +20 -0
- data/app/views/katalyst/content/figures/_figure.html+form.erb +17 -0
- data/app/views/katalyst/content/groups/_group.html+form.erb +18 -0
- data/app/views/katalyst/content/items/_item.html+form.erb +18 -0
- data/app/views/katalyst/content/sections/_section.html+form.erb +18 -0
- data/app/views/katalyst/navigation/items/_button.html.erb +15 -0
- data/app/views/katalyst/navigation/items/_heading.html.erb +11 -0
- data/app/views/katalyst/navigation/items/_link.html.erb +13 -0
- data/app/views/katalyst/navigation/menus/edit.html.erb +12 -0
- data/app/views/katalyst/navigation/menus/new.html.erb +9 -0
- data/app/views/katalyst/navigation/menus/show.html.erb +18 -0
- data/app/views/layouts/koi/_environment.html.erb +4 -0
- data/app/views/layouts/koi/_flash.html.erb +8 -0
- data/app/views/layouts/koi/_header.html.erb +11 -0
- data/app/views/layouts/koi/_navigation.html.erb +13 -0
- data/app/views/layouts/koi/_navigation_collapse.html.erb +3 -0
- data/app/views/layouts/koi/_navigation_header.html.erb +6 -0
- data/app/views/layouts/koi/_navigation_item.html.erb +12 -0
- data/app/views/layouts/koi/application.html.erb +59 -0
- data/app/views/layouts/koi/login.html.erb +29 -0
- data/config/importmap.rb +9 -0
- data/config/initializers/flipper.rb +13 -0
- data/config/initializers/pagy.rb +1 -0
- data/config/initializers/time_formats.rb +5 -0
- data/config/locales/koi.en.yml +18 -0
- data/config/locales/pagy.en.yml +6 -0
- data/config/routes.rb +25 -0
- data/db/migrate/20120220130849_devise_create_admins.rb +56 -0
- data/db/migrate/20130509235316_add_url_rewriter.rb +13 -0
- data/db/migrate/20230213053854_convert_devise_admins_to_rails.rb +7 -0
- data/db/migrate/20230412023411_create_admin_user_credentials.rb +20 -0
- data/db/migrate/20230531063707_update_admin_users.rb +37 -0
- data/db/migrate/20230602033610_add_archived_to_admin_users.rb +7 -0
- data/db/seeds.rb +9 -0
- data/lib/generators/koi/active_record/active_record_generator.rb +43 -0
- data/lib/generators/koi/admin/USAGE +8 -0
- data/lib/generators/koi/admin/admin_generator.rb +20 -0
- data/lib/generators/koi/admin_controller/USAGE +17 -0
- data/lib/generators/koi/admin_controller/admin_controller_generator.rb +51 -0
- data/lib/generators/koi/admin_controller/templates/controller.rb.tt +81 -0
- data/lib/generators/koi/admin_controller/templates/controller_spec.rb.tt +135 -0
- data/lib/generators/koi/admin_route/admin_route_generator.rb +62 -0
- data/lib/generators/koi/admin_views/USAGE +12 -0
- data/lib/generators/koi/admin_views/admin_views_generator.rb +54 -0
- data/lib/generators/koi/admin_views/templates/_fields.html.erb.tt +3 -0
- data/lib/generators/koi/admin_views/templates/_record.html+row.erb.tt +10 -0
- data/lib/generators/koi/admin_views/templates/edit.html.erb.tt +12 -0
- data/lib/generators/koi/admin_views/templates/index.html.erb.tt +7 -0
- data/lib/generators/koi/admin_views/templates/new.html.erb.tt +11 -0
- data/lib/generators/koi/admin_views/templates/show.html.erb.tt +18 -0
- data/lib/govuk_design_system_formbuilder/concerns/file_element.rb +115 -0
- data/lib/govuk_design_system_formbuilder/elements/document.rb +59 -0
- data/lib/govuk_design_system_formbuilder/elements/image.rb +86 -0
- data/lib/katalyst/koi.rb +3 -0
- data/lib/koi/caching.rb +15 -0
- data/lib/koi/config.rb +11 -0
- data/lib/koi/engine.rb +40 -0
- data/lib/koi/form_builder.rb +76 -0
- data/lib/koi/menu/builder.rb +68 -0
- data/lib/koi/menu.rb +46 -0
- data/lib/koi/middleware/url_redirect.rb +44 -0
- data/lib/koi/release.rb +52 -0
- data/lib/koi/version.rb +5 -0
- data/lib/koi.rb +37 -0
- data/spec/factories/admins.rb +9 -0
- data/spec/factories/url_rewrites.rb +9 -0
- metadata +430 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rails/generators/named_base"
|
4
|
+
require "rails/generators/resource_helpers"
|
5
|
+
|
6
|
+
module Koi
|
7
|
+
class AdminViewsGenerator < Rails::Generators::NamedBase
|
8
|
+
include Rails::Generators::ResourceHelpers
|
9
|
+
|
10
|
+
source_root File.expand_path("templates", __dir__)
|
11
|
+
|
12
|
+
argument :attributes, type: :array, default: [], banner: "field:type field:type"
|
13
|
+
|
14
|
+
def create_root_folder
|
15
|
+
empty_directory File.join("app/views/admin", controller_file_path)
|
16
|
+
end
|
17
|
+
|
18
|
+
def copy_view_files
|
19
|
+
available_views.each do |filename|
|
20
|
+
target = filename.gsub("record", singular_name)
|
21
|
+
template filename, File.join("app/views/admin", controller_file_path, target)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def available_views
|
28
|
+
%w(index.html.erb edit.html.erb show.html.erb new.html.erb _fields.html.erb _record.html+row.erb)
|
29
|
+
end
|
30
|
+
|
31
|
+
def govuk_input_for(attribute)
|
32
|
+
case attribute.type
|
33
|
+
when :string
|
34
|
+
%(<%= form.govuk_text_field :#{attribute.name}, label: { size: "s" } %>)
|
35
|
+
when :integer
|
36
|
+
%(<%= form.govuk_number_field :#{attribute.name}, label: { size: "s" } %>)
|
37
|
+
when :boolean
|
38
|
+
%(<%= form.govuk_check_box_field :#{attribute.name}, small: true, label: { size: "s" } %>)
|
39
|
+
when :date
|
40
|
+
%(<%= form.govuk_date_field :#{attribute.name}, legend: { size: "s" } %>)
|
41
|
+
when :text
|
42
|
+
%(<%= form.govuk_rich_text_area :#{attribute.name}, label: { size: "s" } %>)
|
43
|
+
when :rich_text
|
44
|
+
%(<%= form.govuk_rich_text_area :#{attribute.name}, label: { size: "s" } %>)
|
45
|
+
else
|
46
|
+
""
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def index_attributes
|
51
|
+
attributes.select { |attribute| attribute.type == :string }.take(3)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
<%- index_attributes.each_with_index do |attribute, index| -%>
|
2
|
+
<%- if index.zero? -%>
|
3
|
+
<%% row.cell :<%= attribute.name %>, header: true do |cell| %>
|
4
|
+
<%%= link_to cell.value, url_for([:admin, <%= singular_name %>]) %>
|
5
|
+
<%% end %>
|
6
|
+
|
7
|
+
<%- else -%>
|
8
|
+
<%% row.cell :<%= attribute.name %> %>
|
9
|
+
<%- end -%>
|
10
|
+
<%- end -%>
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<%% content_for :header do %>
|
2
|
+
<%%= render(Koi::Header::EditComponent.new(resource: <%= singular_name %>)) %>
|
3
|
+
<%% end %>
|
4
|
+
|
5
|
+
<%%= form_with(model: <%= singular_name %>, url: [:admin, <%= singular_name %>], builder: Koi::FormBuilder) do |form| %>
|
6
|
+
<%%= render "fields", form: %>
|
7
|
+
|
8
|
+
<div class="actions">
|
9
|
+
<%%= form.admin_save %>
|
10
|
+
<%%= form.admin_delete %>
|
11
|
+
</div>
|
12
|
+
<%% end %>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<%% content_for :header do %>
|
2
|
+
<%%= render(Koi::Header::NewComponent.new(model: <%= class_name %>)) %>
|
3
|
+
<%% end %>
|
4
|
+
|
5
|
+
<%%= form_with(model: <%= singular_name %>, url: [:admin, <%= class_name %>], builder: Koi::FormBuilder) do |form| %>
|
6
|
+
<%%= render "fields", form: %>
|
7
|
+
|
8
|
+
<div class="actions">
|
9
|
+
<%%= form.admin_save %>
|
10
|
+
</div>
|
11
|
+
<%% end %>
|
@@ -0,0 +1,18 @@
|
|
1
|
+
<%% content_for :header do %>
|
2
|
+
<%%= render(Koi::Header::ShowComponent.new(resource: <%= singular_name %>)) %>
|
3
|
+
<%% end %>
|
4
|
+
|
5
|
+
<h2>Summary</h2>
|
6
|
+
|
7
|
+
<%%= definition_list(class: "item-table") do |builder| %>
|
8
|
+
<%- attributes.each do |attribute| -%>
|
9
|
+
<%%= builder.item <%= singular_name %>, :<%= attribute.name %> %>
|
10
|
+
<%- end -%>
|
11
|
+
<%% end %>
|
12
|
+
|
13
|
+
<div class="actions">
|
14
|
+
<%%= button_to "Delete", [:admin, <%= singular_name %>],
|
15
|
+
class: "button button--secondary",
|
16
|
+
method: :delete,
|
17
|
+
form: { data: { turbo_confirm: "Are you sure?" } } %>
|
18
|
+
</div>
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "govuk_design_system_formbuilder"
|
4
|
+
|
5
|
+
module GOVUKDesignSystemFormBuilder
|
6
|
+
module Elements
|
7
|
+
module FileElement
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
using PrefixableArray
|
11
|
+
|
12
|
+
include Traits::Error
|
13
|
+
include Traits::Hint
|
14
|
+
include Traits::Label
|
15
|
+
include Traits::Supplemental
|
16
|
+
include Traits::HTMLAttributes
|
17
|
+
include Traits::HTMLClasses
|
18
|
+
include ActionDispatch::Routing::RouteSet::MountedHelpers
|
19
|
+
|
20
|
+
def html
|
21
|
+
Containers::FormGroup.new(*bound, **default_form_group_options(**@form_group)).html do
|
22
|
+
safe_join([label_element, preview, hint_element, error_element, file, destroy_element, supplemental_content])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
protected
|
27
|
+
|
28
|
+
def stimulus_controller_actions
|
29
|
+
<<~ACTIONS.gsub(/\s+/, " ").freeze
|
30
|
+
dragover->#{stimulus_controller}#dragover
|
31
|
+
dragenter->#{stimulus_controller}#dragenter
|
32
|
+
dragleave->#{stimulus_controller}#dragleave
|
33
|
+
drop->#{stimulus_controller}#drop
|
34
|
+
ACTIONS
|
35
|
+
end
|
36
|
+
|
37
|
+
def stimulus_controller
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def file
|
42
|
+
@builder.file_field(@attribute_name, attributes(@html_attributes))
|
43
|
+
end
|
44
|
+
|
45
|
+
def destroy_element
|
46
|
+
return if @html_attributes[:optional].blank?
|
47
|
+
|
48
|
+
@builder.fields_for(:"#{@attribute_name}_attachment") do |form|
|
49
|
+
form.hidden_field :_destroy, value: false, data: { "#{stimulus_controller}_target" => "destroy" }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def destroy_element_trigger
|
54
|
+
return if @html_attributes[:optional].blank?
|
55
|
+
|
56
|
+
content_tag(:button, "", class: "file-destroy", data: { action: "#{stimulus_controller}#setDestroy" })
|
57
|
+
end
|
58
|
+
|
59
|
+
def preview
|
60
|
+
""
|
61
|
+
end
|
62
|
+
|
63
|
+
def preview_url
|
64
|
+
preview? ? main_app.url_for(value) : ""
|
65
|
+
end
|
66
|
+
|
67
|
+
def preview?
|
68
|
+
value&.attached? && value&.persisted?
|
69
|
+
end
|
70
|
+
|
71
|
+
def value
|
72
|
+
@builder.object.send(@attribute_name)
|
73
|
+
end
|
74
|
+
|
75
|
+
def file_input_options
|
76
|
+
default_file_input_options = options
|
77
|
+
|
78
|
+
add_option(default_file_input_options, :accept, @mime_types.join(","))
|
79
|
+
add_option(default_file_input_options, :data, :action, "change->#{stimulus_controller}#onUpload")
|
80
|
+
|
81
|
+
default_file_input_options
|
82
|
+
end
|
83
|
+
|
84
|
+
def options
|
85
|
+
{
|
86
|
+
id: field_id(link_errors: true),
|
87
|
+
class: classes,
|
88
|
+
aria: { describedby: combine_references(hint_id, error_id, supplemental_id) },
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
def classes
|
93
|
+
build_classes(%(file-upload), %(file-upload--error) => has_errors?).prefix(brand)
|
94
|
+
end
|
95
|
+
|
96
|
+
def default_form_group_options(**form_group_options)
|
97
|
+
add_option(form_group_options, :class, "govuk-form-group govuk-image-field")
|
98
|
+
add_option(form_group_options, :data, :controller, stimulus_controller)
|
99
|
+
add_option(form_group_options, :data, :action, stimulus_controller_actions)
|
100
|
+
add_option(form_group_options, :data, :"#{stimulus_controller}_mime_types_value",
|
101
|
+
@mime_types.to_json)
|
102
|
+
|
103
|
+
form_group_options
|
104
|
+
end
|
105
|
+
|
106
|
+
def add_option(options, key, *path)
|
107
|
+
if path.length > 1
|
108
|
+
add_option(options[key] ||= {}, *path)
|
109
|
+
else
|
110
|
+
options[key] = [options[key], *path].compact.join(" ")
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "govuk_design_system_formbuilder"
|
4
|
+
|
5
|
+
module GOVUKDesignSystemFormBuilder
|
6
|
+
module Elements
|
7
|
+
class Document < Base
|
8
|
+
include FileElement
|
9
|
+
|
10
|
+
MIME_TYPES = %w[
|
11
|
+
image/png image/gif image/jpeg image/webp
|
12
|
+
application/pdf
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
def initialize(builder, object_name, attribute_name, hint:, label:, caption:, form_group:, **kwargs, &block)
|
16
|
+
super(builder, object_name, attribute_name, &block)
|
17
|
+
|
18
|
+
@mime_types = MIME_TYPES || kwargs[:mime_types]
|
19
|
+
@label = label
|
20
|
+
@caption = caption
|
21
|
+
@hint = hint
|
22
|
+
@html_attributes = kwargs.merge(file_input_options)
|
23
|
+
@form_group = form_group
|
24
|
+
end
|
25
|
+
|
26
|
+
def preview
|
27
|
+
options = {}
|
28
|
+
add_option(options, :data, "#{stimulus_controller}_target", "preview")
|
29
|
+
add_option(options, :class, "preview-file")
|
30
|
+
add_option(options, :class, "hidden") unless preview?
|
31
|
+
|
32
|
+
tag.div **options do
|
33
|
+
filename = @builder.object.send(@attribute_name).filename.to_s
|
34
|
+
tag.p(filename, class: "preview-filename") + destroy_element_trigger
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def stimulus_controller
|
39
|
+
"document-field"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
module GOVUKDesignSystemFormBuilder
|
46
|
+
module Builder
|
47
|
+
delegate :config, to: GOVUKDesignSystemFormBuilder
|
48
|
+
|
49
|
+
# Generates a +div+ element with an +input+ with +type=file+ with a label, optional hint.
|
50
|
+
#
|
51
|
+
# @example A upload field with label as a proc
|
52
|
+
# = f.govuk_file_field :data, label: -> { tag.h3('Upload your document') }
|
53
|
+
#
|
54
|
+
def govuk_document_field(attribute_name, label: {}, caption: {}, hint: {}, form_group: {}, **kwargs, &block)
|
55
|
+
Elements::Document.new(self, object_name, attribute_name, label:, caption:, hint:, form_group:, **kwargs,
|
56
|
+
&block).html
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "govuk_design_system_formbuilder"
|
4
|
+
require "govuk_design_system_formbuilder/elements/document"
|
5
|
+
|
6
|
+
module GOVUKDesignSystemFormBuilder
|
7
|
+
module Elements
|
8
|
+
class Image < Base
|
9
|
+
include FileElement
|
10
|
+
|
11
|
+
MIME_TYPES = %w[image/png image/gif image/jpeg image/webp].freeze
|
12
|
+
|
13
|
+
def initialize(builder, object_name, attribute_name, hint:, label:, caption:, form_group:, **kwargs, &block)
|
14
|
+
super(builder, object_name, attribute_name, &block)
|
15
|
+
|
16
|
+
@mime_types = kwargs[:mime_types] || MIME_TYPES
|
17
|
+
@label = label
|
18
|
+
@caption = caption
|
19
|
+
@hint = hint
|
20
|
+
@html_attributes = kwargs.merge(file_input_options)
|
21
|
+
@form_group = form_group
|
22
|
+
end
|
23
|
+
|
24
|
+
def preview
|
25
|
+
options = {}
|
26
|
+
add_option(options, :data, "#{stimulus_controller}_target", "preview")
|
27
|
+
add_option(options, :class, "preview-image")
|
28
|
+
add_option(options, :class, "hidden") unless preview?
|
29
|
+
|
30
|
+
tag.div **options do
|
31
|
+
tag.img(src: preview_url, class: "image-thumbnail") + destroy_element_trigger
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def stimulus_controller
|
36
|
+
"image-field"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module GOVUKDesignSystemFormBuilder
|
43
|
+
module Builder
|
44
|
+
delegate :config, to: GOVUKDesignSystemFormBuilder
|
45
|
+
|
46
|
+
# Generates a +div+ element to preview uploaded images and an +input+ with +type=file+ with a label, optional hint.
|
47
|
+
#
|
48
|
+
# @param attribute_name [Symbol] The name of the attribute
|
49
|
+
# @param hint [Hash,Proc] The content of the hint. No hint will be added if 'text' is left +nil+. When a +Proc+ is
|
50
|
+
# supplied the hint will be wrapped in a +div+ instead of a +span+
|
51
|
+
# @option hint text [String] the hint text
|
52
|
+
# @option hint kwargs [Hash] additional arguments are applied as attributes to the hint
|
53
|
+
# @param label [Hash,Proc] configures or sets the associated label content
|
54
|
+
# @option label text [String] the label text
|
55
|
+
# @option label size [String] the size of the label font, can be +xl+, +l+, +m+, +s+ or nil
|
56
|
+
# @option label tag [Symbol,String] the label's wrapper tag, intended to allow labels to act as page headings
|
57
|
+
# @option label hidden [Boolean] control the visibility of the label. Hidden labels will still be read by screen
|
58
|
+
# readers
|
59
|
+
# @option label kwargs [Hash] additional arguments are applied as attributes on the +label+ element
|
60
|
+
# @param caption [Hash] configures or sets the caption content which is inserted above the label
|
61
|
+
# @option caption text [String] the caption text
|
62
|
+
# @option caption size [String] the size of the caption, can be +xl+, +l+ or +m+. Defaults to +m+
|
63
|
+
# @option caption kwargs [Hash] additional arguments are applied as attributes on the caption +span+ element
|
64
|
+
# @option kwargs [Hash] kwargs additional arguments are applied as attributes to the +input+ element.
|
65
|
+
# @param form_group [Hash] configures the form group
|
66
|
+
# @option form_group classes [Array,String] sets the form group's classes
|
67
|
+
# @option form_group kwargs [Hash] additional attributes added to the form group
|
68
|
+
# @param block [Block] arbitrary HTML that will be rendered between the hint and the input
|
69
|
+
# @return [ActiveSupport::SafeBuffer] HTML output
|
70
|
+
#
|
71
|
+
# @example An image field with injected content
|
72
|
+
# = f.govuk_image_field :incident_image,
|
73
|
+
# label: { text: 'Attach a picture of the incident' } do
|
74
|
+
#
|
75
|
+
# p.govuk-inset-text
|
76
|
+
# | If you don't know exactly leave this section blank
|
77
|
+
#
|
78
|
+
# @example A image upload field with label as a proc
|
79
|
+
# = f.govuk_image_field :image, label: -> { tag.h3('Upload your image') }
|
80
|
+
#
|
81
|
+
def govuk_image_field(attribute_name, label: {}, caption: {}, hint: {}, form_group: {}, **kwargs, &block)
|
82
|
+
Elements::Image.new(self, object_name, attribute_name,
|
83
|
+
label:, caption:, hint:, form_group:, **kwargs, &block).html
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/lib/katalyst/koi.rb
ADDED
data/lib/koi/caching.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/integer/time"
|
4
|
+
|
5
|
+
module Koi
|
6
|
+
module Caching
|
7
|
+
# Caching Enabled
|
8
|
+
mattr_accessor :enabled
|
9
|
+
@@enabled = true
|
10
|
+
|
11
|
+
# Cache Expires in
|
12
|
+
mattr_accessor :expires_in
|
13
|
+
@@expires_in = 60.minutes
|
14
|
+
end
|
15
|
+
end
|
data/lib/koi/config.rb
ADDED
data/lib/koi/engine.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "middleware/url_redirect"
|
4
|
+
|
5
|
+
module Koi
|
6
|
+
class Engine < ::Rails::Engine
|
7
|
+
engine_name "koi"
|
8
|
+
|
9
|
+
initializer "koi.assets" do |app|
|
10
|
+
app.middleware.use ::ActionDispatch::Static, "#{root}/public"
|
11
|
+
app.middleware.insert_before Rack::Sendfile, Koi::UrlRedirect
|
12
|
+
|
13
|
+
config.after_initialize do |a|
|
14
|
+
if a.config.respond_to?(:assets)
|
15
|
+
a.config.assets.precompile += %w(koi.js)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
initializer "koi.importmap", before: "importmap" do |app|
|
21
|
+
app.config.importmap.paths << root.join("config/importmap.rb")
|
22
|
+
app.config.importmap.cache_sweepers << root.join("app/assets/javascripts")
|
23
|
+
end
|
24
|
+
|
25
|
+
initializer "koi.factories", after: "factory_bot.set_factory_paths" do
|
26
|
+
FactoryBot.definition_file_paths << Engine.root.join("spec/factories") if defined?(FactoryBot)
|
27
|
+
end
|
28
|
+
|
29
|
+
config.generators do |g|
|
30
|
+
g.test_framework :rspec
|
31
|
+
g.fixture_replacement :factory_bot
|
32
|
+
g.factory_bot dir: "spec/factories"
|
33
|
+
end
|
34
|
+
|
35
|
+
config.to_prepare do
|
36
|
+
Katalyst::Navigation::BaseController.include Koi::Controller::IsAdminController
|
37
|
+
Katalyst::Navigation::BaseController.helper Koi::Engine.helpers
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Koi
|
4
|
+
class FormBuilder < ActionView::Helpers::FormBuilder
|
5
|
+
delegate_missing_to :@template
|
6
|
+
|
7
|
+
include GOVUKDesignSystemFormBuilder::Builder
|
8
|
+
|
9
|
+
# Generates a submit button for saving admin resources.
|
10
|
+
def admin_save(text = "Save", name: :commit, value: :save, class: "button button--primary", **kwargs)
|
11
|
+
button(text, name:, value:, class:, **kwargs)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Generates a delete link formatted as a button that will perform a turbo
|
15
|
+
# delete with a confirmation.
|
16
|
+
def admin_delete(text = "Delete", url: nil, confirm: "Are you sure?", data: {}, **kwargs)
|
17
|
+
return unless object.persisted?
|
18
|
+
|
19
|
+
link_to(text, url || url_for(action: :destroy),
|
20
|
+
class: "button button--secondary",
|
21
|
+
data: data.reverse_merge(turbo_method: :delete, turbo_confirm: confirm),
|
22
|
+
**kwargs)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Generates an archive link formatted as a button that will perform a turbo
|
26
|
+
# delete with a confirmation.
|
27
|
+
def admin_archive(text = "Archive", **kwargs)
|
28
|
+
admin_delete(text, **kwargs)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Generates a discard changes link formatted as a text button that navigates
|
32
|
+
# the user back to the previous page.
|
33
|
+
def admin_discard(text = "Discard", url: :back, **kwargs)
|
34
|
+
link_to(text, url, class: "button button--text", **kwargs)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @api internal
|
38
|
+
# @see GOVUKDesignSystemFormBuilder::Builder#govuk_document_field
|
39
|
+
def govuk_document_field(attribute_name, hint: {}, **kwargs, &block)
|
40
|
+
max_size = hint[:max_size] || App::PERMITTED_IMAGE_SIZE
|
41
|
+
super(attribute_name, hint:, **kwargs) do
|
42
|
+
if block
|
43
|
+
concat(yield)
|
44
|
+
else
|
45
|
+
concat(t("helpers.hint.default.document", max_size: @template.number_to_human_size(max_size)))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# @api internal
|
51
|
+
# @see GOVUKDesignSystemFormBuilder::Builder#govuk_image_field
|
52
|
+
def govuk_image_field(attribute_name, hint: {}, **kwargs, &block)
|
53
|
+
max_size = hint[:max_size] || App::PERMITTED_IMAGE_SIZE
|
54
|
+
super(attribute_name, hint:, **kwargs) do
|
55
|
+
if block
|
56
|
+
concat(yield)
|
57
|
+
else
|
58
|
+
concat(t("helpers.hint.default.image", max_size: @template.number_to_human_size(max_size)))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Use content editor trix setup by default.
|
64
|
+
#
|
65
|
+
# @api internal
|
66
|
+
# @see GOVUKDesignSystemFormBuilder::Builder#govuk_rich_text_area
|
67
|
+
def govuk_rich_text_area(attribute_name, data: {}, **kwargs, &block)
|
68
|
+
data = data.reverse_merge(
|
69
|
+
direct_upload_url: @template.katalyst_content.direct_uploads_url,
|
70
|
+
controller: "content--editor--trix",
|
71
|
+
action: "trix-initialize->content--editor--trix#trixInitialize",
|
72
|
+
)
|
73
|
+
super(attribute_name, data:, **kwargs, &block)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Koi
|
4
|
+
module Menu
|
5
|
+
class Builder
|
6
|
+
def initialize
|
7
|
+
@menu = Katalyst::Navigation::Menu.new
|
8
|
+
@index = 0
|
9
|
+
@depth = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def add_items(items)
|
13
|
+
items.each do |k, v|
|
14
|
+
add_item(k, v)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_item(title, value)
|
19
|
+
if value.is_a?(Hash)
|
20
|
+
add_menu(title:) do |b|
|
21
|
+
value.each do |k, v|
|
22
|
+
b.add_item(k, v)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
else
|
26
|
+
add_link(title:, url: value)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def add_menu(title:, **options, &)
|
31
|
+
@menu.items.build(type: Katalyst::Navigation::Heading.name,
|
32
|
+
title:,
|
33
|
+
**options,
|
34
|
+
index: @index,
|
35
|
+
depth: @depth)
|
36
|
+
@index += 1
|
37
|
+
@depth += 1
|
38
|
+
yield(self)
|
39
|
+
@depth -= 1
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_link(title:, url:, **options)
|
43
|
+
@menu.items.build(type: Katalyst::Navigation::Link.name,
|
44
|
+
title:,
|
45
|
+
url:,
|
46
|
+
**options,
|
47
|
+
index: @index,
|
48
|
+
depth: @depth)
|
49
|
+
@index += 1
|
50
|
+
end
|
51
|
+
|
52
|
+
def add_button(title:, url:, **options)
|
53
|
+
@menu.items.build(type: Katalyst::Navigation::Button.name,
|
54
|
+
title:,
|
55
|
+
url:,
|
56
|
+
**options,
|
57
|
+
index: @index,
|
58
|
+
depth: @depth)
|
59
|
+
@index += 1
|
60
|
+
end
|
61
|
+
|
62
|
+
def render
|
63
|
+
@menu.published_version = @menu.build_draft_version
|
64
|
+
@menu
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/koi/menu.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "koi/menu/builder"
|
4
|
+
|
5
|
+
module Koi
|
6
|
+
module Menu
|
7
|
+
mattr_accessor :priority
|
8
|
+
@@priority = {}
|
9
|
+
|
10
|
+
mattr_accessor :modules
|
11
|
+
@@modules = {}
|
12
|
+
|
13
|
+
mattr_accessor :advanced
|
14
|
+
@@advanced = {}
|
15
|
+
|
16
|
+
def admin_menu(context)
|
17
|
+
builder = Builder.new
|
18
|
+
builder.add_menu(title: "Priority") do |b|
|
19
|
+
b.add_link(title: "View site", url: "/", target: :blank)
|
20
|
+
b.add_link(title: "Dashboard", url: context.main_app.admin_dashboard_path)
|
21
|
+
b.add_items(priority)
|
22
|
+
b.add_button(title: "Logout", url: context.main_app.admin_session_path, http_method: :delete)
|
23
|
+
end
|
24
|
+
builder.add_menu(title: "Modules") do |b|
|
25
|
+
b.add_items(modules)
|
26
|
+
end
|
27
|
+
builder.add_menu(title: "Advanced") do |b|
|
28
|
+
b.add_items(advanced)
|
29
|
+
|
30
|
+
if Object.const_defined?("Flipper::UI")
|
31
|
+
b.add_link(title: "Flipper", url: context.main_app.admin_root_path.concat("/flipper"),
|
32
|
+
target: :blank)
|
33
|
+
end
|
34
|
+
if Object.const_defined?("Sidekiq::Web")
|
35
|
+
b.add_link(title: "Sidekiq", url: context.main_app.admin_root_path.concat("sidekiq"),
|
36
|
+
target: :blank)
|
37
|
+
end
|
38
|
+
b.add_button(title: "Clear cache", url: context.main_app.admin_cache_path,
|
39
|
+
http_method: :delete)
|
40
|
+
end
|
41
|
+
builder.render
|
42
|
+
end
|
43
|
+
|
44
|
+
module_function(:admin_menu)
|
45
|
+
end
|
46
|
+
end
|