katalyst-koi 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|