iron-cms 0.17.2 → 0.18.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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +88 -4
  3. data/app/assets/builds/iron.css +255 -106
  4. data/app/assets/tailwind/iron/application.css +1 -0
  5. data/app/assets/tailwind/iron/components/file-upload.css +26 -0
  6. data/app/assets/tailwind/iron/lexxy.css +111 -87
  7. data/app/controllers/concerns/iron/schema_editing.rb +19 -0
  8. data/app/controllers/iron/api/schema/base_controller.rb +25 -0
  9. data/app/controllers/iron/api/schema/block_definitions_controller.rb +49 -0
  10. data/app/controllers/iron/api/schema/content_types_controller.rb +64 -0
  11. data/app/controllers/iron/api/schema/field_definitions_controller.rb +88 -0
  12. data/app/controllers/iron/api/schema/locales_controller.rb +52 -0
  13. data/app/controllers/iron/application_controller.rb +1 -1
  14. data/app/controllers/iron/block_definitions_controller.rb +1 -0
  15. data/app/controllers/iron/content_types_controller.rb +1 -0
  16. data/app/controllers/iron/field_definitions_controller.rb +2 -1
  17. data/app/controllers/iron/first_runs_controller.rb +1 -1
  18. data/app/controllers/iron/locales_controller.rb +1 -0
  19. data/app/controllers/iron/sessions_controller.rb +1 -1
  20. data/app/helpers/iron/avatar_helper.rb +24 -2
  21. data/app/javascript/iron/controllers/file_upload_controller.js +38 -7
  22. data/app/models/iron/account.rb +1 -1
  23. data/app/models/iron/api/openapi_spec.rb +37 -15
  24. data/app/models/iron/block_definition/exportable.rb +1 -1
  25. data/app/models/iron/block_definition.rb +3 -1
  26. data/app/models/iron/content.rb +176 -0
  27. data/app/models/iron/content_type/exportable.rb +3 -1
  28. data/app/models/iron/content_type/importable.rb +1 -1
  29. data/app/models/iron/content_type.rb +6 -1
  30. data/app/models/iron/entry/content_assignable.rb +10 -2
  31. data/app/models/iron/exporter.rb +1 -26
  32. data/app/models/iron/field/length_constrained.rb +17 -0
  33. data/app/models/iron/field/validatable.rb +16 -0
  34. data/app/models/iron/field.rb +1 -1
  35. data/app/models/iron/field_definition/exportable.rb +3 -4
  36. data/app/models/iron/field_definition/importable.rb +16 -9
  37. data/app/models/iron/field_definition/ranked.rb +1 -1
  38. data/app/models/iron/field_definition/searchable.rb +2 -0
  39. data/app/models/iron/field_definition/validatable.rb +40 -0
  40. data/app/models/iron/field_definition/validations.rb +175 -0
  41. data/app/models/iron/field_definition.rb +1 -1
  42. data/app/models/iron/field_definitions/date.rb +2 -0
  43. data/app/models/iron/field_definitions/file.rb +3 -11
  44. data/app/models/iron/field_definitions/number.rb +2 -0
  45. data/app/models/iron/field_definitions/reference.rb +2 -0
  46. data/app/models/iron/field_definitions/rich_text_area.rb +1 -0
  47. data/app/models/iron/field_definitions/text_area.rb +2 -0
  48. data/app/models/iron/field_definitions/text_field.rb +1 -9
  49. data/app/models/iron/fields/block.rb +5 -1
  50. data/app/models/iron/fields/block_list.rb +5 -1
  51. data/app/models/iron/fields/date.rb +19 -2
  52. data/app/models/iron/fields/file.rb +5 -3
  53. data/app/models/iron/fields/number.rb +28 -1
  54. data/app/models/iron/fields/reference.rb +2 -0
  55. data/app/models/iron/fields/rich_text_area.rb +2 -0
  56. data/app/models/iron/fields/text_area.rb +4 -0
  57. data/app/models/iron/fields/text_field.rb +9 -5
  58. data/app/models/iron/importer.rb +1 -54
  59. data/app/models/iron/locale.rb +2 -0
  60. data/app/models/iron/schema/auto_dumpable.rb +26 -0
  61. data/app/models/iron/schema/diff.rb +194 -0
  62. data/app/models/iron/schema/validation.rb +214 -0
  63. data/app/models/iron/schema.rb +282 -0
  64. data/app/models/iron/seed.rb +5 -3
  65. data/app/models/iron/system.rb +7 -0
  66. data/app/models/iron/user/deactivatable.rb +5 -0
  67. data/app/models/iron/user.rb +18 -1
  68. data/app/views/iron/api/fields/_rich_text_area.json.jbuilder +2 -2
  69. data/app/views/iron/api/schema/block_definitions/_block_definition.json.jbuilder +4 -0
  70. data/app/views/iron/api/schema/block_definitions/index.json.jbuilder +1 -0
  71. data/app/views/iron/api/schema/block_definitions/show.json.jbuilder +1 -0
  72. data/app/views/iron/api/schema/content_types/_content_type.json.jbuilder +5 -0
  73. data/app/views/iron/api/schema/content_types/index.json.jbuilder +1 -0
  74. data/app/views/iron/api/schema/content_types/show.json.jbuilder +1 -0
  75. data/app/views/iron/api/schema/field_definitions/_field_definition.json.jbuilder +7 -0
  76. data/app/views/iron/api/schema/field_definitions/show.json.jbuilder +1 -0
  77. data/app/views/iron/api/schema/locales/_locale.json.jbuilder +4 -0
  78. data/app/views/iron/api/schema/locales/index.json.jbuilder +1 -0
  79. data/app/views/iron/api/schema/locales/show.json.jbuilder +1 -0
  80. data/app/views/iron/block_definitions/_empty_state.html.erb +5 -3
  81. data/app/views/iron/block_definitions/_form.html.erb +3 -1
  82. data/app/views/iron/block_definitions/edit.html.erb +19 -17
  83. data/app/views/iron/block_definitions/index.html.erb +7 -3
  84. data/app/views/iron/block_definitions/show.html.erb +14 -8
  85. data/app/views/iron/content_types/_content_type.html.erb +1 -1
  86. data/app/views/iron/content_types/_empty_state.html.erb +5 -3
  87. data/app/views/iron/content_types/_form.html.erb +12 -8
  88. data/app/views/iron/content_types/edit.html.erb +19 -17
  89. data/app/views/iron/content_types/index.html.erb +7 -3
  90. data/app/views/iron/content_types/show.html.erb +14 -8
  91. data/app/views/iron/entries/_empty_state.html.erb +1 -1
  92. data/app/views/iron/entries/edit.html.erb +1 -1
  93. data/app/views/iron/entries/entry.html.erb +1 -1
  94. data/app/views/iron/entries/fields/_block.html.erb +4 -0
  95. data/app/views/iron/entries/fields/_block_list.html.erb +2 -0
  96. data/app/views/iron/entries/fields/_boolean.html.erb +1 -0
  97. data/app/views/iron/entries/fields/_date.html.erb +3 -2
  98. data/app/views/iron/entries/fields/_field_errors.html.erb +7 -0
  99. data/app/views/iron/entries/fields/_field_label.html.erb +8 -0
  100. data/app/views/iron/entries/fields/_file.html.erb +11 -19
  101. data/app/views/iron/entries/fields/_number.html.erb +6 -2
  102. data/app/views/iron/entries/fields/_reference.html.erb +2 -1
  103. data/app/views/iron/entries/fields/_reference_list.html.erb +2 -0
  104. data/app/views/iron/entries/fields/_rich_text_area.html.erb +2 -1
  105. data/app/views/iron/entries/fields/_text_area.html.erb +6 -2
  106. data/app/views/iron/entries/fields/_text_field.html.erb +5 -12
  107. data/app/views/iron/field_definitions/_field_definition.html.erb +51 -29
  108. data/app/views/iron/field_definitions/date/_form.html.erb +5 -1
  109. data/app/views/iron/field_definitions/edit.html.erb +10 -8
  110. data/app/views/iron/field_definitions/file/_form.html.erb +6 -0
  111. data/app/views/iron/field_definitions/new.html.erb +5 -3
  112. data/app/views/iron/field_definitions/number/_form.html.erb +23 -1
  113. data/app/views/iron/field_definitions/reference/_form.html.erb +5 -0
  114. data/app/views/iron/field_definitions/rich_text_area/_form.html.erb +5 -0
  115. data/app/views/iron/field_definitions/text_area/_form.html.erb +23 -0
  116. data/app/views/iron/field_definitions/text_field/_form.html.erb +20 -1
  117. data/app/views/iron/home/_content_types.html.erb +1 -1
  118. data/app/views/iron/locales/_form.html.erb +5 -3
  119. data/app/views/iron/locales/edit.html.erb +1 -1
  120. data/app/views/iron/locales/index.html.erb +12 -6
  121. data/app/views/iron/shared/_schema_lock_badge.html.erb +19 -0
  122. data/config/locales/en.yml +13 -1
  123. data/config/locales/it.yml +18 -1
  124. data/config/routes.rb +11 -0
  125. data/db/migrate/20260612131538_create_iron_systems.rb +9 -0
  126. data/exe/iron +5 -0
  127. data/lib/generators/iron/agents/agents_generator.rb +52 -0
  128. data/lib/generators/iron/agents/templates/AGENTS.md +24 -0
  129. data/lib/generators/iron/agents/templates/SKILL.md +423 -0
  130. data/lib/generators/iron/install/install_generator.rb +118 -0
  131. data/lib/generators/iron/install/templates/iron_release.rake +5 -0
  132. data/lib/generators/iron/install/templates/schema.json +12 -0
  133. data/lib/generators/iron/install/templates/seeds.rb +13 -0
  134. data/lib/generators/iron/pages/pages_generator.rb +5 -0
  135. data/lib/generators/iron/pages/templates/pages_controller.rb +1 -1
  136. data/lib/generators/iron/pages/templates/show.html.erb +1 -1
  137. data/lib/install/template.rb +9 -0
  138. data/lib/iron/cli.rb +43 -0
  139. data/lib/iron/version.rb +1 -1
  140. data/lib/tasks/iron_content.rake +82 -0
  141. data/lib/tasks/iron_schema.rake +45 -0
  142. metadata +62 -3
@@ -0,0 +1,7 @@
1
+ module Iron
2
+ class System < ApplicationRecord
3
+ has_one :user, as: :authenticatable, dependent: :destroy, touch: true
4
+
5
+ delegate :active?, to: :user, allow_nil: true
6
+ end
7
+ end
@@ -11,6 +11,11 @@ module Iron::User::Deactivatable
11
11
  return false
12
12
  end
13
13
 
14
+ if iron_system?
15
+ errors.add(:base, :cannot_deactivate_system)
16
+ return false
17
+ end
18
+
14
19
  transaction do
15
20
  sessions.destroy_all
16
21
  update!(active: false)
@@ -5,7 +5,7 @@ module Iron
5
5
 
6
6
  ADMIN_LANGUAGES = %w[en it].freeze
7
7
 
8
- delegated_type :authenticatable, types: %w[Iron::Person Iron::Integration]
8
+ delegated_type :authenticatable, types: %w[Iron::Person Iron::Integration Iron::System]
9
9
 
10
10
  validates :language, inclusion: { in: ADMIN_LANGUAGES }
11
11
 
@@ -15,12 +15,20 @@ module Iron
15
15
 
16
16
  alias_method :person, :iron_person
17
17
  alias_method :integration, :iron_integration
18
+ alias_method :system, :iron_system
18
19
 
19
20
  scope :people, -> { iron_people }
20
21
  scope :integrations, -> { iron_integrations }
22
+ scope :systems, -> { iron_systems }
21
23
 
22
24
  delegate :name, to: :authenticatable
23
25
 
26
+ before_destroy :prevent_system_destruction, prepend: true
27
+
28
+ def self.system
29
+ systems.first || create!(authenticatable: System.new, role: :administrator)
30
+ end
31
+
24
32
  def current?
25
33
  self == Current.user
26
34
  end
@@ -28,5 +36,14 @@ module Iron
28
36
  def initials
29
37
  name[0..1]
30
38
  end
39
+
40
+ private
41
+
42
+ def prevent_system_destruction
43
+ if iron_system?
44
+ errors.add(:base, :cannot_destroy_system)
45
+ throw :abort
46
+ end
47
+ end
31
48
  end
32
49
  end
@@ -1,4 +1,4 @@
1
1
  json.set! handle do
2
- json.html field.rich_text.to_s
3
- json.text field.rich_text.to_plain_text
2
+ json.html field.rich_text&.body&.to_html
3
+ json.text field.rich_text&.to_plain_text
4
4
  end
@@ -0,0 +1,4 @@
1
+ json.extract! block_definition, :id, :name, :handle, :description
2
+ json.field_definitions block_definition.field_definitions, partial: "iron/api/schema/field_definitions/field_definition", as: :field_definition
3
+ json.created_at block_definition.created_at.utc.iso8601
4
+ json.updated_at block_definition.updated_at.utc.iso8601
@@ -0,0 +1 @@
1
+ json.data @block_definitions, partial: "iron/api/schema/block_definitions/block_definition", as: :block_definition
@@ -0,0 +1 @@
1
+ json.partial! "iron/api/schema/block_definitions/block_definition", block_definition: @block_definition
@@ -0,0 +1,5 @@
1
+ json.extract! content_type, :id, :name, :handle, :description, :icon, :base_path, :web_publishing_enabled, :title_field_definition_id, :web_page_title_field_definition_id
2
+ json.type content_type.humanized_type.downcase
3
+ json.field_definitions content_type.field_definitions, partial: "iron/api/schema/field_definitions/field_definition", as: :field_definition
4
+ json.created_at content_type.created_at.utc.iso8601
5
+ json.updated_at content_type.updated_at.utc.iso8601
@@ -0,0 +1 @@
1
+ json.data @content_types, partial: "iron/api/schema/content_types/content_type", as: :content_type
@@ -0,0 +1 @@
1
+ json.partial! "iron/api/schema/content_types/content_type", content_type: @content_type
@@ -0,0 +1,7 @@
1
+ json.extract! field_definition, :id, :name, :handle
2
+ json.type field_definition.type_handle
3
+ json.merge! field_definition.metadata
4
+ json.supported_content_type_ids field_definition.supported_content_type_ids if field_definition.respond_to?(:supported_content_type_ids)
5
+ json.supported_block_definition_ids field_definition.supported_block_definition_ids if field_definition.respond_to?(:supported_block_definition_ids)
6
+ json.created_at field_definition.created_at.utc.iso8601
7
+ json.updated_at field_definition.updated_at.utc.iso8601
@@ -0,0 +1 @@
1
+ json.partial! "iron/api/schema/field_definitions/field_definition", field_definition: @field_definition
@@ -0,0 +1,4 @@
1
+ json.extract! locale, :id, :code, :name
2
+ json.default locale.default?
3
+ json.created_at locale.created_at.utc.iso8601
4
+ json.updated_at locale.updated_at.utc.iso8601
@@ -0,0 +1 @@
1
+ json.data @locales, partial: "iron/api/schema/locales/locale", as: :locale
@@ -0,0 +1 @@
1
+ json.partial! "iron/api/schema/locales/locale", locale: @locale
@@ -11,8 +11,10 @@
11
11
  <%= t(".description") %>
12
12
  </p>
13
13
 
14
- <%= link_to new_block_definition_path, class: "button-primary" do %>
15
- <%= icon "plus", class: "size-4" %>
16
- <%= t(".new_block_definition") %>
14
+ <% if Iron::Schema.editable? %>
15
+ <%= link_to new_block_definition_path, class: "button-primary" do %>
16
+ <%= icon "plus", class: "size-4" %>
17
+ <%= t(".new_block_definition") %>
18
+ <% end %>
17
19
  <% end %>
18
20
  </div>
@@ -47,5 +47,7 @@
47
47
  </div>
48
48
  </div>
49
49
 
50
- <%= render "iron/shared/save_bar", form: form %>
50
+ <% if Iron::Schema.editable? %>
51
+ <%= render "iron/shared/save_bar", form: form %>
52
+ <% end %>
51
53
  <% end %>
@@ -5,24 +5,26 @@
5
5
  <div class="flex items-center justify-between mb-6">
6
6
  <%= link_to t(".breadcrumb"), block_definitions_path, class: "breadcrumb" %>
7
7
 
8
- <el-dropdown class="inline-block">
9
- <button type="button" class="button-ghost button-sm p-2">
10
- <%= icon "ellipsis", class: "size-5" %>
11
- <span class="sr-only"><%= t(".more_actions") %></span>
12
- </button>
8
+ <% if Iron::Schema.editable? %>
9
+ <el-dropdown class="inline-block">
10
+ <button type="button" class="button-ghost button-sm p-2">
11
+ <%= icon "ellipsis", class: "size-5" %>
12
+ <span class="sr-only"><%= t(".more_actions") %></span>
13
+ </button>
13
14
 
14
- <el-menu anchor="bottom end" popover class="w-48 origin-top-right rounded-md bg-white shadow-lg outline-1 outline-black/5 transition transition-discrete [--anchor-gap:--spacing(2)] data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in dark:bg-stone-800 dark:shadow-none dark:-outline-offset-1 dark:outline-white/10">
15
- <div class="py-1">
16
- <%= button_to block_definition_path(@block_definition),
17
- method: :delete,
18
- class: "w-full flex items-center gap-2 px-4 py-2 text-sm text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-500/10",
19
- data: { turbo_confirm: t(".delete_confirm") } do %>
20
- <%= icon "trash", class: "size-4" %>
21
- <%= t(".delete_action") %>
22
- <% end %>
23
- </div>
24
- </el-menu>
25
- </el-dropdown>
15
+ <el-menu anchor="bottom end" popover class="w-48 origin-top-right rounded-md bg-white shadow-lg outline-1 outline-black/5 transition transition-discrete [--anchor-gap:--spacing(2)] data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in dark:bg-stone-800 dark:shadow-none dark:-outline-offset-1 dark:outline-white/10">
16
+ <div class="py-1">
17
+ <%= button_to block_definition_path(@block_definition),
18
+ method: :delete,
19
+ class: "w-full flex items-center gap-2 px-4 py-2 text-sm text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-500/10",
20
+ data: { turbo_confirm: t(".delete_confirm") } do %>
21
+ <%= icon "trash", class: "size-4" %>
22
+ <%= t(".delete_action") %>
23
+ <% end %>
24
+ </div>
25
+ </el-menu>
26
+ </el-dropdown>
27
+ <% end %>
26
28
  </div>
27
29
 
28
30
  <h1 class="page-title-lg"><%= @block_definition.name %></h1>
@@ -10,9 +10,13 @@
10
10
  </p>
11
11
  </div>
12
12
 
13
- <%= link_to new_block_definition_path, class: "button-primary self-start" do %>
14
- <%= icon "plus", class: "size-4" %>
15
- <%= t(".new_block_definition") %>
13
+ <% if Iron::Schema.editable? %>
14
+ <%= link_to new_block_definition_path, class: "button-primary self-start" do %>
15
+ <%= icon "plus", class: "size-4" %>
16
+ <%= t(".new_block_definition") %>
17
+ <% end %>
18
+ <% else %>
19
+ <%= render "iron/shared/schema_lock_badge" %>
16
20
  <% end %>
17
21
  </div>
18
22
 
@@ -5,9 +5,13 @@
5
5
  <div class="flex items-center justify-between mb-6">
6
6
  <%= link_to t(".breadcrumb"), block_definitions_path, class: "breadcrumb" %>
7
7
 
8
- <%= link_to edit_block_definition_path(@block_definition), class: "button-secondary" do %>
9
- <%= icon "pencil", class: "size-4" %>
10
- <%= t("iron.actions.edit") %>
8
+ <% if Iron::Schema.editable? %>
9
+ <%= link_to edit_block_definition_path(@block_definition), class: "button-secondary" do %>
10
+ <%= icon "pencil", class: "size-4" %>
11
+ <%= t("iron.actions.edit") %>
12
+ <% end %>
13
+ <% else %>
14
+ <%= render "iron/shared/schema_lock_badge" %>
11
15
  <% end %>
12
16
  </div>
13
17
 
@@ -41,10 +45,12 @@
41
45
  <% end %>
42
46
  </section>
43
47
 
44
- <%= link_to new_block_definition_field_definition_path(@block_definition),
45
- class: "button-primary",
46
- data: { turbo_frame: :drawer } do %>
47
- <%= icon "plus", class: "size-4" %>
48
- <%= t(".add_field") %>
48
+ <% if Iron::Schema.editable? %>
49
+ <%= link_to new_block_definition_field_definition_path(@block_definition),
50
+ class: "button-primary",
51
+ data: { turbo_frame: :drawer } do %>
52
+ <%= icon "plus", class: "size-4" %>
53
+ <%= t(".add_field") %>
54
+ <% end %>
49
55
  <% end %>
50
56
  </div>
@@ -1,6 +1,6 @@
1
1
  <div id="<%= dom_id content_type %>" class="card-item flex items-center gap-3" data-type="<%= content_type.humanized_type.downcase %>">
2
2
  <div class="content-type-icon">
3
- <%= icon content_type.icon.presence || "file-text", class: "size-4" %>
3
+ <%= icon content_type.display_icon, class: "size-4" %>
4
4
  </div>
5
5
 
6
6
  <div class="flex-1 min-w-0">
@@ -11,8 +11,10 @@
11
11
  <%= t(".description") %>
12
12
  </p>
13
13
 
14
- <%= link_to new_content_type_path, class: "button-primary" do %>
15
- <%= icon "plus", class: "size-4" %>
16
- <%= t(".new_content_type") %>
14
+ <% if Iron::Schema.editable? %>
15
+ <%= link_to new_content_type_path, class: "button-primary" do %>
16
+ <%= icon "plus", class: "size-4" %>
17
+ <%= t(".new_content_type") %>
18
+ <% end %>
17
19
  <% end %>
18
20
  </div>
@@ -105,13 +105,15 @@
105
105
  <%= t(".web_publishing_help") %>
106
106
  </p>
107
107
  </div>
108
- <%= form.toggle :web_publishing_enabled,
109
- "aria-label": t(".web_publishing_toggle"),
110
- data: {
111
- controller: "toggle",
112
- toggle_selector_value: "#web-publishing-fields",
113
- action: "toggle#toggle"
114
- } %>
108
+ <% if Iron::Schema.editable? %>
109
+ <%= form.toggle :web_publishing_enabled,
110
+ "aria-label": t(".web_publishing_toggle"),
111
+ data: {
112
+ controller: "toggle",
113
+ toggle_selector_value: "#web-publishing-fields",
114
+ action: "toggle#toggle"
115
+ } %>
116
+ <% end %>
115
117
  </div>
116
118
 
117
119
  <div id="web-publishing-fields" data-ui="<%= content_type.web_publishing_enabled? ? 'visible' : 'hidden' %>" class="data-[ui~=hidden]:hidden">
@@ -148,5 +150,7 @@
148
150
  </div>
149
151
  <% end %>
150
152
 
151
- <%= render "iron/shared/save_bar", form: form %>
153
+ <% if Iron::Schema.editable? %>
154
+ <%= render "iron/shared/save_bar", form: form %>
155
+ <% end %>
152
156
  <% end %>
@@ -5,24 +5,26 @@
5
5
  <div class="flex items-center justify-between mb-6">
6
6
  <%= link_to t(".breadcrumb"), content_types_path, class: "breadcrumb" %>
7
7
 
8
- <el-dropdown class="inline-block">
9
- <button type="button" class="button-ghost button-sm p-2">
10
- <%= icon "ellipsis", class: "size-5" %>
11
- <span class="sr-only"><%= t(".more_actions") %></span>
12
- </button>
8
+ <% if Iron::Schema.editable? %>
9
+ <el-dropdown class="inline-block">
10
+ <button type="button" class="button-ghost button-sm p-2">
11
+ <%= icon "ellipsis", class: "size-5" %>
12
+ <span class="sr-only"><%= t(".more_actions") %></span>
13
+ </button>
13
14
 
14
- <el-menu anchor="bottom end" popover class="w-48 origin-top-right rounded-md bg-white shadow-lg outline-1 outline-black/5 transition transition-discrete [--anchor-gap:--spacing(2)] data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in dark:bg-stone-800 dark:shadow-none dark:-outline-offset-1 dark:outline-white/10">
15
- <div class="py-1">
16
- <%= button_to content_type_path(@content_type),
17
- method: :delete,
18
- class: "w-full flex items-center gap-2 px-4 py-2 text-sm text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-500/10",
19
- data: { turbo_confirm: t(".delete_confirm") } do %>
20
- <%= icon "trash", class: "size-4" %>
21
- <%= t(".delete_action") %>
22
- <% end %>
23
- </div>
24
- </el-menu>
25
- </el-dropdown>
15
+ <el-menu anchor="bottom end" popover class="w-48 origin-top-right rounded-md bg-white shadow-lg outline-1 outline-black/5 transition transition-discrete [--anchor-gap:--spacing(2)] data-closed:scale-95 data-closed:transform data-closed:opacity-0 data-enter:duration-100 data-enter:ease-out data-leave:duration-75 data-leave:ease-in dark:bg-stone-800 dark:shadow-none dark:-outline-offset-1 dark:outline-white/10">
16
+ <div class="py-1">
17
+ <%= button_to content_type_path(@content_type),
18
+ method: :delete,
19
+ class: "w-full flex items-center gap-2 px-4 py-2 text-sm text-red-600 hover:bg-red-50 dark:text-red-400 dark:hover:bg-red-500/10",
20
+ data: { turbo_confirm: t(".delete_confirm") } do %>
21
+ <%= icon "trash", class: "size-4" %>
22
+ <%= t(".delete_action") %>
23
+ <% end %>
24
+ </div>
25
+ </el-menu>
26
+ </el-dropdown>
27
+ <% end %>
26
28
  </div>
27
29
 
28
30
  <h1 class="page-title-lg"><%= @content_type.name %></h1>
@@ -10,9 +10,13 @@
10
10
  </p>
11
11
  </div>
12
12
 
13
- <%= link_to new_content_type_path, class: "button-primary self-start" do %>
14
- <%= icon "plus", class: "size-4" %>
15
- <%= t(".new_content_type") %>
13
+ <% if Iron::Schema.editable? %>
14
+ <%= link_to new_content_type_path, class: "button-primary self-start" do %>
15
+ <%= icon "plus", class: "size-4" %>
16
+ <%= t(".new_content_type") %>
17
+ <% end %>
18
+ <% else %>
19
+ <%= render "iron/shared/schema_lock_badge" %>
16
20
  <% end %>
17
21
  </div>
18
22
 
@@ -5,9 +5,13 @@
5
5
  <div class="flex items-center justify-between mb-6">
6
6
  <%= link_to t(".breadcrumb"), content_types_path, class: "breadcrumb" %>
7
7
 
8
- <%= link_to edit_content_type_path(@content_type), class: "button-secondary" do %>
9
- <%= icon "pencil", class: "size-4" %>
10
- <%= t("iron.actions.edit") %>
8
+ <% if Iron::Schema.editable? %>
9
+ <%= link_to edit_content_type_path(@content_type), class: "button-secondary" do %>
10
+ <%= icon "pencil", class: "size-4" %>
11
+ <%= t("iron.actions.edit") %>
12
+ <% end %>
13
+ <% else %>
14
+ <%= render "iron/shared/schema_lock_badge" %>
11
15
  <% end %>
12
16
  </div>
13
17
 
@@ -41,10 +45,12 @@
41
45
  <% end %>
42
46
  </section>
43
47
 
44
- <%= link_to new_content_type_field_definition_path(@content_type),
45
- class: "button-primary",
46
- data: { turbo_frame: :drawer } do %>
47
- <%= icon "plus", class: "size-4" %>
48
- <%= t(".add_field") %>
48
+ <% if Iron::Schema.editable? %>
49
+ <%= link_to new_content_type_field_definition_path(@content_type),
50
+ class: "button-primary",
51
+ data: { turbo_frame: :drawer } do %>
52
+ <%= icon "plus", class: "size-4" %>
53
+ <%= t(".add_field") %>
54
+ <% end %>
49
55
  <% end %>
50
56
  </div>
@@ -2,7 +2,7 @@
2
2
 
3
3
  <div class="flex flex-col items-center justify-center py-16 px-6 text-center">
4
4
  <div class="flex items-center justify-center size-16 rounded-2xl bg-stone-100 dark:bg-stone-800 mb-6">
5
- <%= icon content_type.icon.presence || "file-text", class: "size-8 text-stone-400 dark:text-stone-500" %>
5
+ <%= icon content_type.display_icon, class: "size-8 text-stone-400 dark:text-stone-500" %>
6
6
  </div>
7
7
 
8
8
  <h3 class="text-lg font-semibold text-stone-900 dark:text-white mb-2">
@@ -54,7 +54,7 @@
54
54
  <div class="mt-4 flex flex-wrap items-center gap-2">
55
55
  <% if @entry.creator %>
56
56
  <%= badge do %>
57
- <%= icon "circle-user", class: "size-3.5" %>
57
+ <%= user_icon @entry.creator, class: "size-3.5" %>
58
58
  <%= @entry.creator.name %>
59
59
  <% end %>
60
60
  <% end %>
@@ -34,7 +34,7 @@
34
34
  <div class="flex flex-wrap items-center gap-2">
35
35
  <% if @entry.creator %>
36
36
  <span class="inline-flex items-center gap-1.5 rounded-full bg-stone-100 px-3 py-1 text-xs font-medium text-stone-500 dark:bg-stone-800 dark:text-stone-400">
37
- <%= icon "circle-user", class: "size-3.5" %>
37
+ <%= user_icon @entry.creator, class: "size-3.5" %>
38
38
  <%= @entry.creator.name %>
39
39
  </span>
40
40
  <% end %>
@@ -55,6 +55,8 @@
55
55
  <%= builder.hidden_field :definition_type %>
56
56
  <%= builder.hidden_field :type %>
57
57
 
58
+ <%= render "iron/entries/fields/field_errors", field: block %>
59
+
58
60
  <div class="block-content">
59
61
  <% block.definition.field_definitions.each do |definition| %>
60
62
  <% block_field = block.find_or_build_field(definition) %>
@@ -81,6 +83,8 @@
81
83
  <%= builder.hidden_field :definition_type %>
82
84
  <%= builder.hidden_field :type %>
83
85
 
86
+ <%= render "iron/entries/fields/field_errors", field: block %>
87
+
84
88
  <% if block.definition.supported_block_definition.present? %>
85
89
  <div class="flex flex-col gap-4">
86
90
  <% block.definition.supported_block_definition.field_definitions.each do |definition| %>
@@ -40,6 +40,8 @@
40
40
  <%= builder.hidden_field :definition_type %>
41
41
  <%= builder.hidden_field :type %>
42
42
 
43
+ <%= render "iron/entries/fields/field_errors", field: block_list %>
44
+
43
45
  <div
44
46
  id="<%= dom_id(block_list.definition) %>"
45
47
  class="space-y-3"
@@ -10,4 +10,5 @@
10
10
  <%= builder.check_box :value_boolean %>
11
11
  <%= builder.label :value_boolean, field.definition.name %>
12
12
  </div>
13
+ <%= render "iron/entries/fields/field_errors", field: field %>
13
14
  </div>
@@ -6,6 +6,7 @@
6
6
  <%= builder.hidden_field :definition_type %>
7
7
  <%= builder.hidden_field :type %>
8
8
 
9
- <%= builder.label :value_datetime, field.definition.name %>
9
+ <%= render "iron/entries/fields/field_label", builder: builder, field: field, attribute: :value_datetime %>
10
10
  <%= builder.datetime_field :value_datetime, class: "input" %>
11
- </div>
11
+ <%= render "iron/entries/fields/field_errors", field: field %>
12
+ </div>
@@ -0,0 +1,7 @@
1
+ <%# locals: (field:) -%>
2
+
3
+ <% if field.errors.any? %>
4
+ <div class="text-red-500 text-sm font-medium mt-1">
5
+ <%= field.errors.full_messages.join(", ") %>
6
+ </div>
7
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <%# locals: (builder:, field:, attribute:) -%>
2
+
3
+ <%= builder.label attribute do %>
4
+ <%= field.definition.name %>
5
+ <% if field.definition.required? %>
6
+ <span class="text-red-500 ml-1">*</span>
7
+ <% end %>
8
+ <% end %>
@@ -6,10 +6,8 @@
6
6
  <%= builder.hidden_field :definition_type %>
7
7
  <%= builder.hidden_field :type %>
8
8
 
9
- <%= builder.label :file do %>
10
- <%= field.definition.name %>
11
- <% end %>
12
- <% if field.definition.file_scope == "specific" %>
9
+ <%= render "iron/entries/fields/field_label", builder: builder, field: field, attribute: :file %>
10
+ <% if field.definition.accepted_extensions.present? %>
13
11
  <p class="text-xs text-stone-500 dark:text-stone-400 mt-1 mb-2">
14
12
  <%= field.definition.validation_description %>
15
13
  </p>
@@ -49,27 +47,21 @@
49
47
  class="h-full w-full object-cover object-center"
50
48
  data-file-upload-target="preview"
51
49
  >
50
+ <div class="h-full w-full flex-col items-center justify-center gap-2 p-4 text-stone-500 dark:text-stone-400" data-file-upload-target="documentPreview">
51
+ <%= icon "file", class: "size-10", data: { file_icon: "file" } %>
52
+ <%= icon "file-text", class: "size-10", data: { file_icon: "pdf" } %>
53
+ <%= icon "file-audio", class: "size-10", data: { file_icon: "audio" } %>
54
+ <%= icon "file-video", class: "size-10", data: { file_icon: "video" } %>
55
+ <p class="text-sm font-medium truncate max-w-full" data-file-upload-target="filename"></p>
56
+ </div>
52
57
  </div>
53
58
  <% end %>
54
59
 
55
60
  <%= builder.file_field :file,
56
61
  class: "hidden",
57
- accept:
58
- (
59
- if field.definition.file_scope == "specific"
60
- field
61
- .definition
62
- .accepted_extensions
63
- &.map { |ext| ".#{ext}" }
64
- &.join(",")
65
- else
66
- nil
67
- end
68
- ),
62
+ accept: field.definition.accepted_extensions&.map { |ext| ".#{ext}" }&.join(","),
69
63
  data: {
70
64
  action: "file-upload#preview",
71
65
  } %>
72
- <% if field.errors.any? %>
73
- <p class="text-sm text-red-600 dark:text-red-400 mt-2"><%= field.errors.full_messages.join(", ") %></p>
74
- <% end %>
66
+ <%= render "iron/entries/fields/field_errors", field: field %>
75
67
  </div>
@@ -6,6 +6,10 @@
6
6
  <%= builder.hidden_field :definition_type %>
7
7
  <%= builder.hidden_field :type %>
8
8
 
9
- <%= builder.label :value_float, field.definition.name %>
10
- <%= builder.number_field :value_float %>
9
+ <%= render "iron/entries/fields/field_label", builder: builder, field: field, attribute: :value_float %>
10
+ <%= builder.number_field :value_float,
11
+ min: field.definition.min,
12
+ max: field.definition.max,
13
+ step: "any" %>
14
+ <%= render "iron/entries/fields/field_errors", field: field %>
11
15
  </div>
@@ -6,7 +6,8 @@
6
6
  <%= builder.hidden_field :definition_type %>
7
7
  <%= builder.hidden_field :type %>
8
8
 
9
- <%= builder.label :referenced_entry_id, field.definition.name %>
9
+ <%= render "iron/entries/fields/field_label", builder: builder, field: field, attribute: :referenced_entry_id %>
10
10
  <% entries = Iron::Entry.with_content_type(field.definition.supported_content_types.pluck(:id)) %>
11
11
  <%= builder.collection_select :referenced_entry_id, entries, :id, :title, { include_blank: "Select an entry" } %>
12
+ <%= render "iron/entries/fields/field_errors", field: field %>
12
13
  </div>
@@ -29,6 +29,8 @@
29
29
  <%= builder.hidden_field :definition_type %>
30
30
  <%= builder.hidden_field :type %>
31
31
 
32
+ <%= render "iron/entries/fields/field_errors", field: reference_list %>
33
+
32
34
  <div
33
35
  id="<%= reference_list_dom_id %>"
34
36
  class="space-y-2"
@@ -6,6 +6,7 @@
6
6
  <%= builder.hidden_field :definition_type %>
7
7
  <%= builder.hidden_field :type %>
8
8
 
9
- <%= builder.label :rich_text, field.definition.name %>
9
+ <%= render "iron/entries/fields/field_label", builder: builder, field: field, attribute: :rich_text %>
10
10
  <%= builder.rich_textarea :rich_text, class: "prose prose-stone dark:prose-invert min-h-56 max-w-none w-full" %>
11
+ <%= render "iron/entries/fields/field_errors", field: field %>
11
12
  </div>
@@ -6,6 +6,10 @@
6
6
  <%= builder.hidden_field :definition_type %>
7
7
  <%= builder.hidden_field :type %>
8
8
 
9
- <%= builder.label :value_text, field.definition.name %>
10
- <%= builder.textarea :value_text, rows: 3 %>
9
+ <%= render "iron/entries/fields/field_label", builder: builder, field: field, attribute: :value_text %>
10
+ <%= builder.textarea :value_text,
11
+ rows: 3,
12
+ minlength: field.definition.min_length,
13
+ maxlength: field.definition.max_length %>
14
+ <%= render "iron/entries/fields/field_errors", field: field %>
11
15
  </div>