lato_cms 3.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.
Files changed (62) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +67 -0
  4. data/Rakefile +6 -0
  5. data/app/assets/config/lato_cms_manifest.js +2 -0
  6. data/app/assets/javascripts/lato_cms/application.js +1 -0
  7. data/app/assets/javascripts/lato_cms/controllers/lato_cms_advanced_editor_controller.js +57 -0
  8. data/app/assets/javascripts/lato_cms/controllers/lato_cms_color_field_controller.js +9 -0
  9. data/app/assets/javascripts/lato_cms/controllers/lato_cms_component_accordion_controller.js +60 -0
  10. data/app/assets/javascripts/lato_cms/controllers/lato_cms_component_form_controller.js +96 -0
  11. data/app/assets/javascripts/lato_cms/controllers/lato_cms_component_toggle_controller.js +89 -0
  12. data/app/assets/javascripts/lato_cms/controllers/lato_cms_gallery_field_controller.js +136 -0
  13. data/app/assets/javascripts/lato_cms/controllers/lato_cms_image_field_controller.js +24 -0
  14. data/app/assets/javascripts/lato_cms/controllers/lato_cms_json_field_controller.js +27 -0
  15. data/app/assets/javascripts/lato_cms/controllers/lato_cms_page_preview_controller.js +86 -0
  16. data/app/assets/javascripts/lato_cms/controllers/lato_cms_text_field_controller.js +74 -0
  17. data/app/assets/stylesheets/lato_cms/application.scss +252 -0
  18. data/app/controllers/lato_cms/api/pages_controller.rb +38 -0
  19. data/app/controllers/lato_cms/application_controller.rb +26 -0
  20. data/app/controllers/lato_cms/pages_controller.rb +194 -0
  21. data/app/helpers/lato_cms/application_helper.rb +4 -0
  22. data/app/helpers/lato_cms/pages_helper.rb +69 -0
  23. data/app/jobs/lato_cms/application_job.rb +4 -0
  24. data/app/mailers/lato_cms/application_mailer.rb +6 -0
  25. data/app/models/lato_cms/page.rb +133 -0
  26. data/app/models/lato_cms/page_field.rb +113 -0
  27. data/app/models/lato_cms/template_manager.rb +64 -0
  28. data/app/views/lato_cms/pages/_component_accordion.html.erb +97 -0
  29. data/app/views/lato_cms/pages/_fields_editor.html.erb +12 -0
  30. data/app/views/lato_cms/pages/_form_create.html.erb +26 -0
  31. data/app/views/lato_cms/pages/_form_update.html.erb +39 -0
  32. data/app/views/lato_cms/pages/create.html.erb +13 -0
  33. data/app/views/lato_cms/pages/fields/_boolean.html.erb +16 -0
  34. data/app/views/lato_cms/pages/fields/_color.html.erb +18 -0
  35. data/app/views/lato_cms/pages/fields/_date.html.erb +16 -0
  36. data/app/views/lato_cms/pages/fields/_datetime.html.erb +16 -0
  37. data/app/views/lato_cms/pages/fields/_file.html.erb +29 -0
  38. data/app/views/lato_cms/pages/fields/_gallery.html.erb +55 -0
  39. data/app/views/lato_cms/pages/fields/_image.html.erb +39 -0
  40. data/app/views/lato_cms/pages/fields/_json.html.erb +23 -0
  41. data/app/views/lato_cms/pages/fields/_multiselect.html.erb +25 -0
  42. data/app/views/lato_cms/pages/fields/_number.html.erb +18 -0
  43. data/app/views/lato_cms/pages/fields/_select.html.erb +23 -0
  44. data/app/views/lato_cms/pages/fields/_string.html.erb +18 -0
  45. data/app/views/lato_cms/pages/fields/_text.html.erb +80 -0
  46. data/app/views/lato_cms/pages/fields/_textarea.html.erb +16 -0
  47. data/app/views/lato_cms/pages/index.html.erb +32 -0
  48. data/app/views/lato_cms/pages/show.html.erb +105 -0
  49. data/app/views/lato_cms/pages/update.html.erb +13 -0
  50. data/config/importmap.rb +2 -0
  51. data/config/locales/en.yml +70 -0
  52. data/config/locales/it.yml +70 -0
  53. data/config/routes.rb +22 -0
  54. data/db/migrate/20250328072343_add_lato_cms_admin_to_lato_user.rb +5 -0
  55. data/db/migrate/20260320070819_create_lato_cms_pages.rb +13 -0
  56. data/db/migrate/20260323171241_create_lato_cms_page_fields.rb +13 -0
  57. data/lib/lato_cms/config.rb +13 -0
  58. data/lib/lato_cms/engine.rb +13 -0
  59. data/lib/lato_cms/version.rb +3 -0
  60. data/lib/lato_cms.rb +15 -0
  61. data/lib/tasks/lato_cms_tasks.rake +183 -0
  62. metadata +158 -0
@@ -0,0 +1,29 @@
1
+ <% label = field_config['name'] || field_id.humanize %>
2
+ <% required = field_config['required'] == true %>
3
+ <% settings = field_config['settings'] || {} %>
4
+ <% multiple = settings['multiple'] != false %>
5
+ <% accept = settings['accept'] %>
6
+
7
+ <label class="form-label">
8
+ <%= label %><%= ' *' if required %>
9
+ </label>
10
+
11
+ <% if page_field&.files&.attached? %>
12
+ <div class="mb-2">
13
+ <% page_field.files.each do |file| %>
14
+ <div class="form-check mb-1 p-2 ps-4 bg-light rounded">
15
+ <input type="checkbox" class="form-check-input" name="fields[<%= field_id %>][remove_file_ids][]" value="<%= file.id %>" id="remove_file_<%= file.id %>">
16
+ <label class="form-check-label small" for="remove_file_<%= file.id %>">
17
+ <i class="bi bi-paperclip me-1"></i><%= file.blob.filename %> (<%= number_to_human_size(file.blob.byte_size) %>) — <span class="text-danger"><%= t('lato_cms.field_file_remove') %></span>
18
+ </label>
19
+ </div>
20
+ <% end %>
21
+ </div>
22
+ <% end %>
23
+
24
+ <input type="file"
25
+ class="form-control"
26
+ name="fields[<%= field_id %>][files][]"
27
+ id="fields_<%= field_id %>_files"
28
+ <%= 'multiple' if multiple %>
29
+ <% if accept %>accept="<%= accept %>"<% end %>>
@@ -0,0 +1,55 @@
1
+ <% label = field_config['name'] || field_id.humanize %>
2
+ <% required = field_config['required'] == true %>
3
+ <% settings = field_config['settings'] || {} %>
4
+ <% accept = settings['accept'] || 'image/*' %>
5
+
6
+ <%# Build ordered file list from value JSON or default order %>
7
+ <% ordered_files = begin
8
+ if page_field&.files&.attached?
9
+ order = JSON.parse(page_field.value || '[]') rescue []
10
+ files = page_field.files.to_a
11
+ order.filter_map { |id| files.find { |f| f.id.to_s == id.to_s } } +
12
+ files.reject { |f| order.include?(f.id.to_s) }
13
+ else
14
+ []
15
+ end
16
+ end %>
17
+
18
+ <label class="form-label">
19
+ <%= label %><%= ' *' if required %>
20
+ </label>
21
+
22
+ <div data-controller="lato-cms-gallery-field" data-lato-cms-gallery-field-field-id-value="<%= field_id %>">
23
+ <div class="lato-cms-gallery-field__grid mb-2"
24
+ data-lato-cms-gallery-field-target="grid"
25
+ data-action="dragover->lato-cms-gallery-field#onGridDragOver drop->lato-cms-gallery-field#onGridDrop">
26
+ <% ordered_files.each do |file| %>
27
+ <div class="lato-cms-gallery-field__item"
28
+ data-gallery-item
29
+ data-attachment-id="<%= file.id %>"
30
+ draggable="true"
31
+ data-action="dragstart->lato-cms-gallery-field#onDragStart dragend->lato-cms-gallery-field#onDragEnd">
32
+ <img src="<%= lato_cms_attachment_path(file) %>" class="lato-cms-gallery-field__thumb" alt="<%= file.filename %>">
33
+ <button type="button" class="lato-cms-gallery-field__remove" data-action="click->lato-cms-gallery-field#remove">
34
+ <i class="bi bi-x-lg"></i>
35
+ </button>
36
+ </div>
37
+ <% end %>
38
+ </div>
39
+
40
+ <% if ordered_files.empty? %>
41
+ <p class="text-muted small mb-2" data-lato-cms-gallery-field-target="emptyMsg"><%= t('lato_cms.field_gallery_empty') %></p>
42
+ <% end %>
43
+
44
+ <label class="btn btn-sm btn-outline-secondary">
45
+ <i class="bi bi-plus-lg me-1"></i><%= t('lato_cms.field_gallery_add_images') %>
46
+ <input type="file"
47
+ class="d-none"
48
+ name="fields[<%= field_id %>][files][]"
49
+ accept="<%= accept %>"
50
+ multiple
51
+ data-lato-cms-gallery-field-target="fileInput"
52
+ data-action="change->lato-cms-gallery-field#addFiles">
53
+ </label>
54
+ <small class="text-muted ms-2"><%= t('lato_cms.field_gallery_drag_to_reorder') %></small>
55
+ </div>
@@ -0,0 +1,39 @@
1
+ <% label = field_config['name'] || field_id.humanize %>
2
+ <% required = field_config['required'] == true %>
3
+ <% settings = field_config['settings'] || {} %>
4
+ <% accept = settings['accept'] || 'image/*' %>
5
+ <% current_file = page_field&.files&.attached? ? page_field.files.first : nil %>
6
+
7
+ <label class="form-label">
8
+ <%= label %><%= ' *' if required %>
9
+ </label>
10
+
11
+ <div data-controller="lato-cms-image-field">
12
+ <% if current_file %>
13
+ <div class="mb-2" data-lato-cms-image-field-target="currentContainer">
14
+ <div class="position-relative d-inline-block">
15
+ <img src="<%= lato_cms_attachment_path(current_file) %>" class="img-thumbnail lato-cms-image-field__thumb" data-lato-cms-image-field-target="currentImage" alt="<%= current_file.filename %>">
16
+ <label class="lato-cms-image-field__remove-btn" title="<%= t('lato_cms.field_image_remove') %>">
17
+ <input type="checkbox" class="d-none" name="fields[<%= field_id %>][remove_file_ids][]" value="<%= current_file.id %>" data-lato-cms-image-field-target="removeCheck" data-action="change->lato-cms-image-field#toggleRemove">
18
+ <i class="bi bi-x-lg"></i>
19
+ </label>
20
+ </div>
21
+ </div>
22
+ <% end %>
23
+
24
+ <div class="mb-2 d-none" data-lato-cms-image-field-target="newPreviewContainer">
25
+ <div class="position-relative d-inline-block">
26
+ <img src="" class="img-thumbnail lato-cms-image-field__thumb" data-lato-cms-image-field-target="newPreview" alt="">
27
+ <span class="badge bg-success lato-cms-image-field__new-badge"><%= t('lato_cms.field_image_new_badge') %></span>
28
+ </div>
29
+ </div>
30
+
31
+ <input type="file"
32
+ class="form-control"
33
+ name="fields[<%= field_id %>][files][]"
34
+ id="fields_<%= field_id %>_files"
35
+ accept="<%= accept %>"
36
+ data-lato-cms-image-field-target="input"
37
+ data-action="change->lato-cms-image-field#preview"
38
+ <%= 'required' if required && !current_file %>>
39
+ </div>
@@ -0,0 +1,23 @@
1
+ <% label = field_config['name'] || field_id.humanize %>
2
+ <% required = field_config['required'] == true %>
3
+ <% settings = field_config['settings'] || {} %>
4
+ <% current_value = page_field&.value || '' %>
5
+ <% if current_value.present? %>
6
+ <% begin; current_value = JSON.pretty_generate(JSON.parse(current_value)); rescue JSON::ParserError; end %>
7
+ <% end %>
8
+
9
+ <label class="form-label" for="fields_<%= field_id %>_value">
10
+ <%= label %><%= ' *' if required %>
11
+ </label>
12
+ <div data-controller="lato-cms-json-field">
13
+ <textarea
14
+ class="form-control font-monospace"
15
+ name="fields[<%= field_id %>][value]"
16
+ id="fields_<%= field_id %>_value"
17
+ rows="<%= settings['rows'] || 6 %>"
18
+ data-lato-cms-json-field-target="input"
19
+ data-action="input->lato-cms-json-field#validate"
20
+ <%= 'required' if required %>
21
+ <% if settings['placeholder'] %>placeholder="<%= settings['placeholder'] %>"<% end %>><%= current_value %></textarea>
22
+ <div data-lato-cms-json-field-target="status" class="form-text"></div>
23
+ </div>
@@ -0,0 +1,25 @@
1
+ <% label = field_config['name'] || field_id.humanize %>
2
+ <% required = field_config['required'] == true %>
3
+ <% settings = field_config['settings'] || {} %>
4
+ <% options = settings['options'] || [] %>
5
+ <% current_values = begin; page_field&.value.present? ? JSON.parse(page_field.value) : []; rescue; []; end %>
6
+
7
+ <label class="form-label" for="fields_<%= field_id %>_value">
8
+ <%= label %><%= ' *' if required %>
9
+ </label>
10
+ <select
11
+ class="form-select"
12
+ name="fields[<%= field_id %>][value][]"
13
+ id="fields_<%= field_id %>_value"
14
+ multiple
15
+ size="<%= [options.length, 6].min %>"
16
+ <%= 'required' if required %>>
17
+ <% options.each do |option| %>
18
+ <% if option.is_a?(Hash) %>
19
+ <option value="<%= option['value'] %>" <%= 'selected' if current_values.include?(option['value'].to_s) %>><%= option['label'] %></option>
20
+ <% else %>
21
+ <option value="<%= option %>" <%= 'selected' if current_values.include?(option.to_s) %>><%= option %></option>
22
+ <% end %>
23
+ <% end %>
24
+ </select>
25
+ <small class="form-text text-muted"><%= t('lato_cms.field_multiselect_hint') %></small>
@@ -0,0 +1,18 @@
1
+ <% label = field_config['name'] || field_id.humanize %>
2
+ <% required = field_config['required'] == true %>
3
+ <% settings = field_config['settings'] || {} %>
4
+ <% current_value = page_field&.value || '' %>
5
+
6
+ <label class="form-label" for="fields_<%= field_id %>_value">
7
+ <%= label %><%= ' *' if required %>
8
+ </label>
9
+ <input type="number"
10
+ class="form-control"
11
+ name="fields[<%= field_id %>][value]"
12
+ id="fields_<%= field_id %>_value"
13
+ value="<%= current_value %>"
14
+ <%= 'required' if required %>
15
+ <% if settings['min'] %>min="<%= settings['min'] %>"<% end %>
16
+ <% if settings['max'] %>max="<%= settings['max'] %>"<% end %>
17
+ <% if settings['step'] %>step="<%= settings['step'] %>"<% end %>
18
+ <% if settings['placeholder'] %>placeholder="<%= settings['placeholder'] %>"<% end %>>
@@ -0,0 +1,23 @@
1
+ <% label = field_config['name'] || field_id.humanize %>
2
+ <% required = field_config['required'] == true %>
3
+ <% settings = field_config['settings'] || {} %>
4
+ <% options = settings['options'] || [] %>
5
+ <% current_value = page_field&.value || '' %>
6
+
7
+ <label class="form-label" for="fields_<%= field_id %>_value">
8
+ <%= label %><%= ' *' if required %>
9
+ </label>
10
+ <select
11
+ class="form-select"
12
+ name="fields[<%= field_id %>][value]"
13
+ id="fields_<%= field_id %>_value"
14
+ <%= 'required' if required %>>
15
+ <option value=""><%= t('lato_cms.field_select_placeholder') %></option>
16
+ <% options.each do |option| %>
17
+ <% if option.is_a?(Hash) %>
18
+ <option value="<%= option['value'] %>" <%= 'selected' if current_value == option['value'].to_s %>><%= option['label'] %></option>
19
+ <% else %>
20
+ <option value="<%= option %>" <%= 'selected' if current_value == option.to_s %>><%= option %></option>
21
+ <% end %>
22
+ <% end %>
23
+ </select>
@@ -0,0 +1,18 @@
1
+ <% label = field_config['name'] || field_id.humanize %>
2
+ <% required = field_config['required'] == true %>
3
+ <% settings = field_config['settings'] || {} %>
4
+ <% current_value = page_field&.value || '' %>
5
+
6
+ <label class="form-label" for="fields_<%= field_id %>_value">
7
+ <%= label %><%= ' *' if required %>
8
+ </label>
9
+ <input type="text"
10
+ class="form-control"
11
+ name="fields[<%= field_id %>][value]"
12
+ id="fields_<%= field_id %>_value"
13
+ value="<%= current_value %>"
14
+ <%= 'required' if required %>
15
+ <% if settings['placeholder'] %>placeholder="<%= settings['placeholder'] %>"<% end %>
16
+ <% if settings['maxlength'] %>maxlength="<%= settings['maxlength'] %>"<% end %>
17
+ <% if settings['minlength'] %>minlength="<%= settings['minlength'] %>"<% end %>
18
+ <% if settings['pattern'] %>pattern="<%= settings['pattern'] %>"<% end %>>
@@ -0,0 +1,80 @@
1
+ <% label = field_config['name'] || field_id.humanize %>
2
+ <% required = field_config['required'] == true %>
3
+ <% current_value = page_field&.value || '' %>
4
+
5
+ <label class="form-label" for="fields_<%= field_id %>_value">
6
+ <%= label %><%= ' *' if required %>
7
+ </label>
8
+
9
+ <div data-controller="lato-cms-text-field">
10
+
11
+ <%# ── Toolbar ──────────────────────────────────────────────────────────── %>
12
+ <div class="lato-cms-wysiwyg-toolbar border border-bottom-0 rounded-top bg-light px-2 py-1 d-flex flex-wrap gap-1 align-items-center"
13
+ data-lato-cms-text-field-target="toolbar">
14
+ <button type="button" class="btn btn-sm btn-outline-secondary py-0 px-1" title="<%= t('lato_cms.field_text_toolbar_bold') %>"
15
+ data-action="click->lato-cms-text-field#bold">
16
+ <i class="bi bi-type-bold"></i>
17
+ </button>
18
+ <button type="button" class="btn btn-sm btn-outline-secondary py-0 px-1" title="<%= t('lato_cms.field_text_toolbar_italic') %>"
19
+ data-action="click->lato-cms-text-field#italic">
20
+ <i class="bi bi-type-italic"></i>
21
+ </button>
22
+ <button type="button" class="btn btn-sm btn-outline-secondary py-0 px-1" title="<%= t('lato_cms.field_text_toolbar_underline') %>"
23
+ data-action="click->lato-cms-text-field#underline">
24
+ <i class="bi bi-type-underline"></i>
25
+ </button>
26
+ <button type="button" class="btn btn-sm btn-outline-secondary py-0 px-1" title="<%= t('lato_cms.field_text_toolbar_strikethrough') %>"
27
+ data-action="click->lato-cms-text-field#strikethrough">
28
+ <i class="bi bi-type-strikethrough"></i>
29
+ </button>
30
+ <span class="vr mx-1"></span>
31
+ <button type="button" class="btn btn-sm btn-outline-secondary py-0 px-1" title="<%= t('lato_cms.field_text_toolbar_align_left') %>"
32
+ data-action="click->lato-cms-text-field#alignLeft">
33
+ <i class="bi bi-text-left"></i>
34
+ </button>
35
+ <button type="button" class="btn btn-sm btn-outline-secondary py-0 px-1" title="<%= t('lato_cms.field_text_toolbar_align_center') %>"
36
+ data-action="click->lato-cms-text-field#alignCenter">
37
+ <i class="bi bi-text-center"></i>
38
+ </button>
39
+ <button type="button" class="btn btn-sm btn-outline-secondary py-0 px-1" title="<%= t('lato_cms.field_text_toolbar_align_right') %>"
40
+ data-action="click->lato-cms-text-field#alignRight">
41
+ <i class="bi bi-text-right"></i>
42
+ </button>
43
+ <span class="vr mx-1"></span>
44
+ <button type="button" class="btn btn-sm btn-outline-secondary py-0 px-1" title="<%= t('lato_cms.field_text_toolbar_unordered_list') %>"
45
+ data-action="click->lato-cms-text-field#insertUnorderedList">
46
+ <i class="bi bi-list-ul"></i>
47
+ </button>
48
+ <button type="button" class="btn btn-sm btn-outline-secondary py-0 px-1" title="<%= t('lato_cms.field_text_toolbar_ordered_list') %>"
49
+ data-action="click->lato-cms-text-field#insertOrderedList">
50
+ <i class="bi bi-list-ol"></i>
51
+ </button>
52
+
53
+ <%# ── Source toggle ────────────────────────────────────────────────── %>
54
+ <span class="vr mx-1 ms-auto"></span>
55
+ <button type="button" class="btn btn-sm btn-outline-secondary py-0 px-1" title="<%= t('lato_cms.field_text_toolbar_toggle_html_source') %>"
56
+ data-action="click->lato-cms-text-field#toggleSource"
57
+ data-lato-cms-text-field-target="sourceBtn">
58
+ <i class="bi bi-code-slash"></i>
59
+ </button>
60
+ </div>
61
+
62
+ <%# ── Editable area (WYSIWYG) ─────────────────────────────────────────── %>
63
+ <div class="form-control lato-cms-wysiwyg-editor rounded-0 rounded-bottom"
64
+ contenteditable="true"
65
+ style="min-height: 150px; overflow-y: auto; line-height: 1.6;"
66
+ data-lato-cms-text-field-target="editor"><%= current_value.html_safe %></div>
67
+
68
+ <%# ── Source view (raw HTML) ──────────────────────────────────────────── %>
69
+ <textarea class="form-control lato-cms-wysiwyg-source rounded-0 rounded-bottom font-monospace d-none"
70
+ style="min-height: 150px; font-size: 0.8rem; resize: vertical; white-space: pre;"
71
+ spellcheck="false"
72
+ data-lato-cms-text-field-target="source"></textarea>
73
+
74
+ <%# ── Hidden input submitted with the form ───────────────────────────── %>
75
+ <input type="hidden"
76
+ name="fields[<%= field_id %>][value]"
77
+ id="fields_<%= field_id %>_value"
78
+ data-lato-cms-text-field-target="input"
79
+ value="<%= CGI.escapeHTML(current_value) %>">
80
+ </div>
@@ -0,0 +1,16 @@
1
+ <% label = field_config['name'] || field_id.humanize %>
2
+ <% required = field_config['required'] == true %>
3
+ <% settings = field_config['settings'] || {} %>
4
+ <% current_value = page_field&.value || '' %>
5
+
6
+ <label class="form-label" for="fields_<%= field_id %>_value">
7
+ <%= label %><%= ' *' if required %>
8
+ </label>
9
+ <textarea
10
+ class="form-control"
11
+ name="fields[<%= field_id %>][value]"
12
+ id="fields_<%= field_id %>_value"
13
+ rows="<%= settings['rows'] || 4 %>"
14
+ <%= 'required' if required %>
15
+ <% if settings['placeholder'] %>placeholder="<%= settings['placeholder'] %>"<% end %>
16
+ <% if settings['maxlength'] %>maxlength="<%= settings['maxlength'] %>"<% end %>><%= current_value %></textarea>
@@ -0,0 +1,32 @@
1
+ <%= lato_page_head t('lato_cms.pages_index_title'), [{ label: t('lato_cms.pages_index_title') }] %>
2
+
3
+ <div class="card">
4
+ <div class="card-header d-flex gap-2">
5
+ <%= link_to lato_cms.pages_path, class: "btn btn-sm #{params[:locale].blank? ? 'btn-primary' : 'btn-outline-secondary'}" do %>
6
+ <i class="bi bi-globe me-1"></i><%= t('lato_cms.pages_filter_all') %>
7
+ <% end %>
8
+ <% LatoCms.config.locales.each do |locale| %>
9
+ <%= link_to lato_cms.pages_path(locale: locale), class: "btn btn-sm #{params[:locale] == locale.to_s ? 'btn-primary' : 'btn-outline-secondary'}" do %>
10
+ <%= locale_to_flag(locale) %> <%= locale.to_s.upcase %>
11
+ <% end %>
12
+ <% end %>
13
+ </div>
14
+ <div class="card-body">
15
+ <%= lato_index @pages,
16
+ custom_actions: {
17
+ create: {
18
+ path: lato_cms.pages_create_path,
19
+ icon: 'bi bi-plus',
20
+ title: t('lato_cms.page_create_cta'),
21
+ data: {
22
+ controller: 'lato-tooltip',
23
+ lato_action_target: 'trigger',
24
+ turbo_frame: dom_id(LatoCms::Page.new, 'form'),
25
+ action_title: t('lato_cms.page_create_title')
26
+ }
27
+ }
28
+ },
29
+ pagination_options: [10, 20, 50, 100]
30
+ %>
31
+ </div>
32
+ </div>
@@ -0,0 +1,105 @@
1
+ <%= lato_page_head @page.title, [
2
+ { label: t('lato_cms.pages_index_title'), path: lato_cms.pages_path },
3
+ { label: @page.title }
4
+ ] %>
5
+
6
+ <div data-controller="lato-cms-advanced-editor">
7
+ <div class="row align-items-stretch lato-cms-advanced-editor__row" data-lato-cms-advanced-editor-target="row">
8
+
9
+ <%# ── Preview column ─────────────────────────────────────────────────── %>
10
+ <div class="col-12 col-lg-6 mb-4 lato-cms-advanced-editor__preview-col" data-lato-cms-advanced-editor-target="previewCol">
11
+ <% if @page.frontend_url.present? %>
12
+ <div class="card h-100" style="overflow: hidden;" data-controller="lato-cms-page-preview">
13
+ <div class="card-body p-0">
14
+ <iframe src="<%= @page.frontend_url %>" data-lato-cms-page-preview-target="iframe" style="width: 100%; height: calc(100dvh - 180px); border: none; display: block;"></iframe>
15
+ </div>
16
+ </div>
17
+ <% else %>
18
+ <div class="card h-100">
19
+ <div class="card-body d-flex flex-column align-items-center justify-content-center text-muted" style="height: calc(100dvh - 180px);">
20
+ <i class="bi bi-globe fs-1 mb-3"></i>
21
+ <p class="mb-1"><%= t('lato_cms.show_no_preview_title') %></p>
22
+ <p class="small"><%= t('lato_cms.show_no_preview_description').html_safe %></p>
23
+ <%= link_to t('lato_cms.show_edit_settings_cta'), lato_cms.pages_update_path(@page), class: 'btn btn-sm btn-outline-primary mt-2',
24
+ data: { lato_action_target: 'trigger', turbo_frame: dom_id(@page, 'form'), action_title: t('lato_cms.page_update_title') } %>
25
+ </div>
26
+ </div>
27
+ <% end %>
28
+ </div>
29
+
30
+ <%# ── Editor column ──────────────────────────────────────────────────── %>
31
+ <div class="col-12 col-lg-6 mb-4 lato-cms-advanced-editor__editor-col" data-lato-cms-advanced-editor-target="editorCol">
32
+ <div class="card h-100">
33
+ <div class="card-header d-flex justify-content-between align-items-center">
34
+ <h2 class="fs-5 mb-0"><%= t('lato_cms.show_editor_title') %></h2>
35
+ <div class="d-flex align-items-center gap-2">
36
+ <%= lato_cms_page_locale(@page) %>
37
+ <span class="vr"></span>
38
+ <%= lato_cms_page_actions(@page, show_edit: true, show_delete: true, hide_show: true) %>
39
+ <span class="vr"></span>
40
+ <button class="btn btn-sm btn-outline-secondary"
41
+ title="<%= t('lato_cms.show_advanced_editor_tooltip') %>"
42
+ data-controller="lato-tooltip"
43
+ data-action="click->lato-cms-advanced-editor#toggle"
44
+ data-lato-cms-advanced-editor-target="toggleBtn">
45
+ <i class="bi bi-fullscreen"></i>
46
+ </button>
47
+ <button class="btn btn-sm btn-outline-secondary d-none"
48
+ title="<%= t('lato_cms.show_hide_panel_tooltip') %>"
49
+ data-controller="lato-tooltip"
50
+ data-action="click->lato-cms-advanced-editor#toggleSidebar"
51
+ data-lato-cms-advanced-editor-target="collapseBtn">
52
+ <i class="bi bi-layout-sidebar-reverse"></i>
53
+ </button>
54
+ </div>
55
+ </div>
56
+ <div class="card-body" style="overflow-y: auto; max-height: calc(100dvh - 230px);">
57
+ <% if @page.template_id.blank? %>
58
+ <div class="text-center text-muted py-5">
59
+ <i class="bi bi-file-earmark-code fs-1 mb-3 d-block"></i>
60
+ <p class="mb-1"><%= t('lato_cms.show_no_template_title') %></p>
61
+ <p class="small"><%= t('lato_cms.show_no_template_description') %></p>
62
+ <%= link_to t('lato_cms.show_edit_settings_cta'), lato_cms.pages_update_path(@page), class: 'btn btn-sm btn-outline-primary mt-2',
63
+ data: { lato_action_target: 'trigger', turbo_frame: dom_id(@page, 'form'), action_title: t('lato_cms.page_update_title') } %>
64
+ </div>
65
+ <% elsif !@page.template_available? %>
66
+ <div class="alert alert-warning">
67
+ <i class="bi bi-exclamation-triangle me-1"></i>
68
+ <%= t('lato_cms.show_template_unavailable', template_id: @page.template_id) %>
69
+ <%= link_to t('lato_cms.show_edit_settings_cta'), lato_cms.pages_update_path(@page), class: 'btn btn-sm btn-outline-warning ms-2',
70
+ data: { lato_action_target: 'trigger', turbo_frame: dom_id(@page, 'form'), action_title: t('lato_cms.page_update_title') } %>
71
+ </div>
72
+ <% existing_fields = @page.fields.group_by(&:template_component_id) %>
73
+ <% if existing_fields.any? %>
74
+ <p class="text-muted small"><%= t('lato_cms.show_previously_saved_fields') %></p>
75
+ <% existing_fields.each do |tc_id, fields| %>
76
+ <div class="mb-3">
77
+ <h6 class="text-muted"><%= tc_id %></h6>
78
+ <% fields.each do |field| %>
79
+ <div class="mb-2 ps-3">
80
+ <small class="text-muted"><%= field.field_id %>:</small>
81
+ <span><%= field.value.present? ? field.value.truncate(100) : '-' %></span>
82
+ </div>
83
+ <% end %>
84
+ </div>
85
+ <% end %>
86
+ <% end %>
87
+ <% else %>
88
+ <%= render 'lato_cms/pages/fields_editor', page: @page, template_components: @template_components %>
89
+ <% end %>
90
+ </div>
91
+ </div>
92
+ </div>
93
+
94
+ </div>
95
+
96
+ <%# ── Re-open sidebar tab (visible only in advanced mode + sidebar closed) %>
97
+ <button class="lato-cms-advanced-editor__reopen-btn"
98
+ hidden
99
+ title="<%= t('lato_cms.show_show_editor_panel_tooltip') %>"
100
+ data-controller="lato-tooltip"
101
+ data-action="click->lato-cms-advanced-editor#toggleSidebar"
102
+ data-lato-cms-advanced-editor-target="reopenBtn">
103
+ <i class="bi bi-chevron-left"></i>
104
+ </button>
105
+ </div>
@@ -0,0 +1,13 @@
1
+ <%= lato_page_head t('lato_cms.page_update_title'), [
2
+ { label: t('lato_cms.pages_index_title'), path: lato_cms.pages_path },
3
+ { label: t('lato_cms.page_update_title') }
4
+ ] %>
5
+
6
+ <div class="card mb-4">
7
+ <div class="card-header">
8
+ <h2 class="fs-4 mb-0"><%= t('lato_cms.page_update_title') %></h2>
9
+ </div>
10
+ <div class="card-body">
11
+ <%= render 'lato_cms/pages/form_update', page: @page %>
12
+ </div>
13
+ </div>
@@ -0,0 +1,2 @@
1
+ pin "lato_cms/application", to: "lato_cms/application.js"
2
+ pin_all_from LatoCms::Engine.root.join("app/assets/javascripts/lato_cms/controllers"), under: "controllers", to: "lato_cms/controllers"
@@ -0,0 +1,70 @@
1
+ en:
2
+ lato_cms:
3
+ cta_show: Show
4
+ cta_edit: Edit
5
+ cta_update: Update
6
+ cta_delete: Delete
7
+ cta_confirm: Confirm
8
+ cta_delete_confirm: Are you sure you want to delete this page?
9
+ page_created: Page created successfully
10
+ page_updated: Page updated successfully
11
+ fields_saved: Fields saved successfully
12
+ page_deleted: Page deleted successfully
13
+ unauthorized_section: You do not have access to this section.
14
+ page_delete_failed: Failed to delete page
15
+ api_page_not_found: Page not found
16
+ pages_index_title: Pages
17
+ pages_filter_all: All
18
+ page_create_title: New page
19
+ page_create_cta: Create
20
+ page_update_title: Edit page
21
+ form_frontend_url: Frontend URL
22
+ form_template: Template
23
+ form_no_template_option: "-- No template --"
24
+ form_template_unavailable: "The template \"%{template_id}\" is no longer available. Please select a different template."
25
+ action_view_frontend: View
26
+ show_no_preview_title: No preview available
27
+ show_no_preview_description: Set the <strong>Frontend URL</strong> to see the page preview.
28
+ show_edit_settings_cta: Edit page settings
29
+ show_editor_title: Editor
30
+ show_advanced_editor_tooltip: Advanced editor
31
+ show_hide_panel_tooltip: Hide panel
32
+ show_show_editor_panel_tooltip: Show editor panel
33
+ show_no_template_title: No template assigned
34
+ show_no_template_description: Assign a template to start editing fields.
35
+ show_template_unavailable: "The template <strong>%{template_id}</strong> is no longer available. Please assign a different template."
36
+ show_previously_saved_fields: "Previously saved fields:"
37
+ fields_component_not_found_badge: Component not found
38
+ component_required_badge: Required
39
+ component_disabled_badge: Disabled
40
+ component_enabled_label: Enabled
41
+ component_active_label: This component is active
42
+ component_inactive_label: This component is not active
43
+ component_disabled_alert: This component is disabled and will not be exposed by API.
44
+ component_state_updated: Component state updated
45
+ component_state_saving: Updating...
46
+ component_state_saved: State updated
47
+ component_state_save_error: Failed to update state
48
+ component_required_cannot_disable: Required component cannot be disabled
49
+ component_disabled_cannot_save: Disabled component cannot be saved
50
+ fields_save_component: Save
51
+ fields_component_missing_alert_html: "Component <strong>%{component_id}</strong> not found. The component configuration may have been removed."
52
+ fields_previously_saved_data: "Previously saved data:"
53
+ field_file_remove: Remove
54
+ field_gallery_empty: No images yet. Add images below.
55
+ field_gallery_add_images: Add images
56
+ field_gallery_drag_to_reorder: Drag to reorder
57
+ field_image_remove: Remove
58
+ field_image_new_badge: New
59
+ field_multiselect_hint: Hold Ctrl/Cmd to select multiple
60
+ field_select_placeholder: "-- Select --"
61
+ field_text_toolbar_bold: Bold
62
+ field_text_toolbar_italic: Italic
63
+ field_text_toolbar_underline: Underline
64
+ field_text_toolbar_strikethrough: Strikethrough
65
+ field_text_toolbar_align_left: Align left
66
+ field_text_toolbar_align_center: Align center
67
+ field_text_toolbar_align_right: Align right
68
+ field_text_toolbar_unordered_list: Unordered list
69
+ field_text_toolbar_ordered_list: Ordered list
70
+ field_text_toolbar_toggle_html_source: Toggle HTML source
@@ -0,0 +1,70 @@
1
+ it:
2
+ lato_cms:
3
+ cta_show: Visualizza
4
+ cta_edit: Modifica
5
+ cta_update: Aggiorna
6
+ cta_delete: Elimina
7
+ cta_confirm: Conferma
8
+ cta_delete_confirm: Sei sicuro di voler eliminare questa pagina?
9
+ page_created: Pagina creata con successo
10
+ page_updated: Pagina aggiornata con successo
11
+ fields_saved: Campi salvati con successo
12
+ page_deleted: Pagina eliminata con successo
13
+ unauthorized_section: Non hai accesso a questa sezione.
14
+ page_delete_failed: Impossibile eliminare la pagina
15
+ api_page_not_found: Pagina non trovata
16
+ pages_index_title: Pagine
17
+ pages_filter_all: Tutte
18
+ page_create_title: Nuova pagina
19
+ page_create_cta: Crea
20
+ page_update_title: Modifica pagina
21
+ form_frontend_url: URL Frontend
22
+ form_template: Template
23
+ form_no_template_option: "-- Nessun template --"
24
+ form_template_unavailable: "Il template \"%{template_id}\" non e piu disponibile. Seleziona un template diverso."
25
+ action_view_frontend: Anteprima
26
+ show_no_preview_title: Anteprima non disponibile
27
+ show_no_preview_description: Imposta il <strong>URL Frontend</strong> per visualizzare l'anteprima della pagina.
28
+ show_edit_settings_cta: Modifica impostazioni pagina
29
+ show_editor_title: Editor
30
+ show_advanced_editor_tooltip: Editor avanzato
31
+ show_hide_panel_tooltip: Nascondi pannello
32
+ show_show_editor_panel_tooltip: Mostra pannello editor
33
+ show_no_template_title: Nessun template assegnato
34
+ show_no_template_description: Assegna un template per iniziare a modificare i campi.
35
+ show_template_unavailable: "Il template <strong>%{template_id}</strong> non e piu disponibile. Assegna un template diverso."
36
+ show_previously_saved_fields: "Campi salvati in precedenza:"
37
+ fields_component_not_found_badge: Componente non trovato
38
+ component_required_badge: Obbligatoria
39
+ component_disabled_badge: Disattivata
40
+ component_enabled_label: Attiva
41
+ component_active_label: Questa componente e attiva
42
+ component_inactive_label: Questa componente non e attiva
43
+ component_disabled_alert: Questa componente e disattivata e non verra esposta dalle API.
44
+ component_state_updated: Stato componente aggiornato
45
+ component_state_saving: Aggiornamento in corso...
46
+ component_state_saved: Stato aggiornato
47
+ component_state_save_error: Errore durante aggiornamento stato
48
+ component_required_cannot_disable: Una componente obbligatoria non puo essere disattivata
49
+ component_disabled_cannot_save: Una componente disattivata non puo essere salvata
50
+ fields_save_component: Salva
51
+ fields_component_missing_alert_html: "Componente <strong>%{component_id}</strong> non trovato. La configurazione del componente potrebbe essere stata rimossa."
52
+ fields_previously_saved_data: "Dati salvati in precedenza:"
53
+ field_file_remove: Rimuovi
54
+ field_gallery_empty: Nessuna immagine presente. Aggiungi immagini qui sotto.
55
+ field_gallery_add_images: Aggiungi immagini
56
+ field_gallery_drag_to_reorder: Trascina per riordinare
57
+ field_image_remove: Rimuovi
58
+ field_image_new_badge: Nuovo
59
+ field_multiselect_hint: Tieni premuto Ctrl/Cmd per selezionare piu opzioni
60
+ field_select_placeholder: "-- Seleziona --"
61
+ field_text_toolbar_bold: Grassetto
62
+ field_text_toolbar_italic: Corsivo
63
+ field_text_toolbar_underline: Sottolineato
64
+ field_text_toolbar_strikethrough: Barrato
65
+ field_text_toolbar_align_left: Allinea a sinistra
66
+ field_text_toolbar_align_center: Allinea al centro
67
+ field_text_toolbar_align_right: Allinea a destra
68
+ field_text_toolbar_unordered_list: Elenco puntato
69
+ field_text_toolbar_ordered_list: Elenco numerato
70
+ field_text_toolbar_toggle_html_source: Mostra/nascondi sorgente HTML