backstage 0.1.15 → 0.1.17

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: da02200425a97de115bbd7a4ff85a2cc907ab221647c3c9d22bb105ce690ff99
4
- data.tar.gz: 4f53000b38b952e825cb5e82211162a7ce2149834e4efc671cf560595226593b
3
+ metadata.gz: daa88b222f8f96e18685ccb3fd5aa7df2b7123a0000e65eb75ed567e3241b4b9
4
+ data.tar.gz: 33a3c9b348029ccb14da21801174769d11481e332735ce923f2b1e2d15799691
5
5
  SHA512:
6
- metadata.gz: 83f557e7644fa1e07487d3995854645f71454fee792023e9a344f24639fe222cef1d9c8a6bb130e8673dac4f7c4a942df5bb52a7ce06b0628c0a74461e3b1a59
7
- data.tar.gz: dfd0f21995ca2a17e92016e86e6c975633b35b54c40a198e9edd8964116080a574055bd8480aceb589d5391739d87ee51cbdb65fcc600c636b3b8e1d4c31894c
6
+ metadata.gz: 889e54bc4ab50f29711be9c3dbf5de0afc06855affbede4bf3fd56d9f8131be3c1877dc273bb39f7448cb428ef9d9bedd15c7e00756d37094a2857396b9326cc
7
+ data.tar.gz: 6569763f092592c84fccb96f4bbd3bf589a97ef26a13c07fbcf1441c39ba1f32805c1ed7a2e638a814f46d68ad7a59646c0a1461ade2d28fbc33d156b71534d7
data/CHANGELOG.md CHANGED
@@ -7,6 +7,26 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.1.17] — 2026-06-04
11
+
12
+ ### Added
13
+
14
+ - Flash notice is displayed after a successful create or update
15
+
16
+ ### Fixed
17
+
18
+ - Edit and new forms now render validation error messages inline when save fails
19
+ - Successful update now redirects back to the edit page instead of the index
20
+
21
+ ## [0.1.16] — 2026-06-02
22
+
23
+ ### Fixed
24
+
25
+ - Nested table now uses a `<template>` element instead of a static new row, so unsaved blank rows are never submitted unintentionally
26
+ - New-row template includes readonly fields as plain inputs so new records can be fully populated via the Add button
27
+ - Readonly nested fields are now permitted in Strong Parameters, so new records with readonly fields save correctly
28
+ - `belongs_to :assoc, readonly: true` now propagates `readonly:` to the generated FK field
29
+
10
30
  ## [0.1.15] — 2026-05-27
11
31
 
12
32
  ### Added
@@ -40,7 +40,8 @@ module Backstage
40
40
  def create
41
41
  @record = @resource_config.model_class.new(record_params)
42
42
  if @record.save
43
- redirect_to edit_resource_path(resource: params[:resource], id: @record.id)
43
+ redirect_to edit_resource_path(resource: params[:resource], id: @record.id),
44
+ notice: "#{@resource_config.model_class.model_name.human} was successfully created."
44
45
  else
45
46
  render :new, status: :unprocessable_entity
46
47
  end
@@ -48,7 +49,8 @@ module Backstage
48
49
 
49
50
  def update
50
51
  if @record.update(record_params)
51
- redirect_to resources_path(resource: params[:resource])
52
+ redirect_to edit_resource_path(resource: params[:resource], id: @record.id),
53
+ notice: "#{@resource_config.model_class.model_name.human} was successfully saved."
52
54
  else
53
55
  render :edit, status: :unprocessable_entity
54
56
  end
@@ -99,8 +101,7 @@ module Backstage
99
101
  elsif field.has_many?
100
102
  [{field.name => []}]
101
103
  elsif field.nested?
102
- writable = field.nested_fields - field.nested_readonly_fields
103
- [{"#{field.name}_attributes": [:id, :_destroy, *writable]}]
104
+ [{"#{field.name}_attributes": [:id, :_destroy, *field.nested_fields]}]
104
105
  else
105
106
  [field.name]
106
107
  end
@@ -27,13 +27,29 @@
27
27
  </td>
28
28
  </tr>
29
29
  <% end %>
30
- <%= f.fields_for field.name, records.klass.new, child_index: records.size do |nf| %>
31
- <tr data-nested-new-row>
32
- <% field.nested_fields.reject { |col| field.nested_readonly_fields.include?(col) }.each do |col| %>
33
- <td><%= nf.text_field col %></td>
30
+ <%# Template is not part of the DOM — never submitted until the user clicks Add %>
31
+ <template data-nested-template data-next-index="<%= records.size %>">
32
+ <tr>
33
+ <% field.nested_fields.each do |col| %>
34
+ <td>
35
+ <input type="text"
36
+ name="<%= "#{f.object_name}[#{field.name}_attributes][__INDEX__][#{col}]" %>"
37
+ value="" />
38
+ </td>
34
39
  <% end %>
35
- <td></td>
40
+ <td>
41
+ <input type="hidden"
42
+ name="<%= "#{f.object_name}[#{field.name}_attributes][__INDEX__][_destroy]" %>"
43
+ value="0"
44
+ data-nested-destroy-flag="true" />
45
+ <button type="button" data-nested-destroy aria-label="Remove">&times;</button>
46
+ </td>
36
47
  </tr>
37
- <% end %>
48
+ </template>
49
+ <tr>
50
+ <td colspan="<%= field.nested_fields.size + 1 %>">
51
+ <button type="button" data-nested-add>+ Add</button>
52
+ </td>
53
+ </tr>
38
54
  </tbody>
39
55
  </table>
@@ -4,6 +4,15 @@
4
4
  <div<% if sidebar&.links&.any? %> class="edit-layout"<% end %>>
5
5
  <div>
6
6
  <%= form_with model: @record, url: resource_path(resource: params[:resource], id: @record.id), method: :patch do |f| %>
7
+ <% if @record.errors.any? %>
8
+ <div class="error-messages">
9
+ <ul>
10
+ <% @record.errors.full_messages.each do |msg| %>
11
+ <li><%= msg %></li>
12
+ <% end %>
13
+ </ul>
14
+ </div>
15
+ <% end %>
7
16
  <% @resource_config.edit_fields.each do |field| %>
8
17
  <% if field.container? %>
9
18
  <%= render partial: field.partial_path, locals: { f: f, field: field, record: @record } %>
@@ -1,6 +1,15 @@
1
1
  <h1>New <%= @resource_config.model_class.model_name.human %></h1>
2
2
 
3
3
  <%= form_with model: @record, url: resources_path(resource: params[:resource]), method: :post do |f| %>
4
+ <% if @record.errors.any? %>
5
+ <div class="error-messages">
6
+ <ul>
7
+ <% @record.errors.full_messages.each do |msg| %>
8
+ <li><%= msg %></li>
9
+ <% end %>
10
+ </ul>
11
+ </div>
12
+ <% end %>
4
13
  <% @resource_config.edit_fields.each do |field| %>
5
14
  <% if field.container? %>
6
15
  <%= render partial: field.partial_path, locals: { f: f, field: field, record: @record } %>
@@ -26,12 +26,23 @@
26
26
  })
27
27
  })
28
28
  })
29
- document.querySelectorAll("[data-nested-destroy]").forEach(function(btn) {
30
- btn.addEventListener("click", function() {
31
- var row = btn.closest("tr")
32
- row.querySelector("[data-nested-destroy-flag]").value = "1"
29
+ document.addEventListener("click", function(e) {
30
+ var destroyBtn = e.target.closest("[data-nested-destroy]")
31
+ if (destroyBtn) {
32
+ var row = destroyBtn.closest("tr")
33
+ var flag = row.querySelector("[data-nested-destroy-flag]")
34
+ if (flag) flag.value = "1"
33
35
  row.hidden = true
34
- })
36
+ }
37
+ var addBtn = e.target.closest("[data-nested-add]")
38
+ if (addBtn) {
39
+ var table = addBtn.closest("table")
40
+ var tmpl = table.querySelector("[data-nested-template]")
41
+ var index = parseInt(tmpl.dataset.nextIndex, 10)
42
+ tmpl.dataset.nextIndex = index + 1
43
+ var html = tmpl.innerHTML.replace(/__INDEX__/g, index)
44
+ addBtn.closest("tr").insertAdjacentHTML("beforebegin", html)
45
+ }
35
46
  })
36
47
  })
37
48
  </script>
@@ -49,6 +60,14 @@
49
60
  </ul>
50
61
  </nav>
51
62
  <main>
63
+ <div id="flash">
64
+ <% if flash[:notice] %>
65
+ <p class="notice"><%= flash[:notice] %></p>
66
+ <% end %>
67
+ <% if flash[:alert] %>
68
+ <p class="alert"><%= flash[:alert] %></p>
69
+ <% end %>
70
+ </div>
52
71
  <%= yield %>
53
72
  </main>
54
73
  </body>
@@ -63,7 +63,7 @@ module Backstage
63
63
  def belongs_to(name, **opts)
64
64
  assoc = AssociationConfig.new(name, :belongs_to, opts)
65
65
  @associations << assoc
66
- fk_field = Field.new(assoc.foreign_key, :belongs_to, association: assoc)
66
+ fk_field = Field.new(assoc.foreign_key, :belongs_to, association: assoc, readonly: opts[:readonly])
67
67
  index_field = Field.new(name.to_sym, :belongs_to, association: assoc)
68
68
  @edit_fields.reject! { |f| f.name == fk_field.name }
69
69
  @index_fields.reject! { |f| f.name == fk_field.name || f.name == index_field.name }
@@ -1,3 +1,3 @@
1
1
  module Backstage
2
- VERSION = "0.1.15"
2
+ VERSION = "0.1.17"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: backstage
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.15
4
+ version: 0.1.17
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gareth James
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-05-27 00:00:00.000000000 Z
11
+ date: 2026-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties