plum-cms 0.1.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 (178) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +20 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +252 -0
  5. data/app/assets/builds/tailwind.css +2 -0
  6. data/app/assets/images/plum-mark.svg +7 -0
  7. data/app/assets/images/table-needs-logo.svg +83 -0
  8. data/app/assets/javascripts/plum/application.js +15 -0
  9. data/app/assets/stylesheets/application.css +10 -0
  10. data/app/assets/stylesheets/plum/control_panel.css +2 -0
  11. data/app/assets/tailwind/application.css +47 -0
  12. data/app/controllers/plum/application_controller.rb +34 -0
  13. data/app/controllers/plum/cp/assets_controller.rb +84 -0
  14. data/app/controllers/plum/cp/base_controller.rb +45 -0
  15. data/app/controllers/plum/cp/content_types_controller.rb +59 -0
  16. data/app/controllers/plum/cp/dashboard_controller.rb +10 -0
  17. data/app/controllers/plum/cp/entries_controller.rb +240 -0
  18. data/app/controllers/plum/cp/form_definitions_controller.rb +105 -0
  19. data/app/controllers/plum/cp/form_submissions_controller.rb +26 -0
  20. data/app/controllers/plum/cp/globals_controller.rb +84 -0
  21. data/app/controllers/plum/cp/nav_items_controller.rb +68 -0
  22. data/app/controllers/plum/cp/nav_menus_controller.rb +56 -0
  23. data/app/controllers/plum/cp/site_settings_controller.rb +87 -0
  24. data/app/controllers/plum/cp/taxonomies_controller.rb +56 -0
  25. data/app/controllers/plum/cp/terms_controller.rb +55 -0
  26. data/app/controllers/plum/cp/theme_previews_controller.rb +27 -0
  27. data/app/controllers/plum/cp/themes_controller.rb +98 -0
  28. data/app/controllers/plum/form_submissions_controller.rb +44 -0
  29. data/app/controllers/plum/pages_controller.rb +161 -0
  30. data/app/controllers/plum/sessions_controller.rb +25 -0
  31. data/app/controllers/plum/theme_assets_controller.rb +14 -0
  32. data/app/helpers/application_helper.rb +2 -0
  33. data/app/helpers/markdown_helper.rb +21 -0
  34. data/app/helpers/plum/cp/assets_helper.rb +21 -0
  35. data/app/helpers/plum/cp/theme_settings_helper.rb +26 -0
  36. data/app/javascript/application.js +18 -0
  37. data/app/javascript/controllers/application.js +9 -0
  38. data/app/javascript/controllers/index.js +4 -0
  39. data/app/javascript/controllers/plum/blocks_editor_controller.js +275 -0
  40. data/app/javascript/controllers/plum/blueprint_controller.js +120 -0
  41. data/app/javascript/controllers/plum/form_fields_controller.js +84 -0
  42. data/app/javascript/controllers/plum/image_picker_controller.js +143 -0
  43. data/app/javascript/controllers/plum/theme_settings_controller.js +17 -0
  44. data/app/models/plum/application_record.rb +5 -0
  45. data/app/models/plum/asset.rb +69 -0
  46. data/app/models/plum/content_type.rb +35 -0
  47. data/app/models/plum/entry.rb +67 -0
  48. data/app/models/plum/entry_term.rb +8 -0
  49. data/app/models/plum/form_definition.rb +68 -0
  50. data/app/models/plum/form_submission.rb +76 -0
  51. data/app/models/plum/global.rb +24 -0
  52. data/app/models/plum/nav_item.rb +58 -0
  53. data/app/models/plum/nav_menu.rb +24 -0
  54. data/app/models/plum/site.rb +112 -0
  55. data/app/models/plum/site_scoped.rb +17 -0
  56. data/app/models/plum/site_setting.rb +35 -0
  57. data/app/models/plum/taxonomy.rb +24 -0
  58. data/app/models/plum/term.rb +23 -0
  59. data/app/models/plum/user.rb +16 -0
  60. data/app/services/plum/base_blocks.rb +161 -0
  61. data/app/services/plum/block_editor_config.rb +65 -0
  62. data/app/services/plum/block_library.rb +31 -0
  63. data/app/services/plum/builder_renderer.rb +63 -0
  64. data/app/services/plum/field_expander.rb +78 -0
  65. data/app/services/plum/form_renderer.rb +110 -0
  66. data/app/services/plum/liquid_context.rb +335 -0
  67. data/app/services/plum/liquid_filters.rb +58 -0
  68. data/app/services/plum/liquid_renderer.rb +49 -0
  69. data/app/services/plum/theme.rb +160 -0
  70. data/app/services/plum/theme_asset_path.rb +31 -0
  71. data/app/services/plum/theme_package_installer.rb +287 -0
  72. data/app/services/plum/theme_registry.rb +63 -0
  73. data/app/services/plum/theme_resolver.rb +55 -0
  74. data/app/services/plum/theme_settings_params.rb +54 -0
  75. data/app/themes/bagel-boy/assets/screenshot.svg +92 -0
  76. data/app/themes/bagel-boy/assets/theme.css +184 -0
  77. data/app/themes/bagel-boy/blocks/hours.liquid +4 -0
  78. data/app/themes/bagel-boy/blocks/menu_item.liquid +7 -0
  79. data/app/themes/bagel-boy/layouts/base.liquid +64 -0
  80. data/app/themes/bagel-boy/templates/collections/posts.liquid +32 -0
  81. data/app/themes/bagel-boy/templates/entries/_default.liquid +13 -0
  82. data/app/themes/bagel-boy/templates/entries/landing.liquid +4 -0
  83. data/app/themes/bagel-boy/templates/entries/pages.liquid +18 -0
  84. data/app/themes/bagel-boy/templates/entries/posts.liquid +8 -0
  85. data/app/themes/bagel-boy/templates/index.liquid +10 -0
  86. data/app/themes/bagel-boy/templates/search.liquid +28 -0
  87. data/app/themes/bagel-boy/theme.yml +47 -0
  88. data/app/themes/bagel-shop/assets/screenshot.svg +28 -0
  89. data/app/themes/bagel-shop/assets/theme.css +7 -0
  90. data/app/themes/bagel-shop/layouts/base.liquid +507 -0
  91. data/app/themes/bagel-shop/templates/entries/_default.liquid +15 -0
  92. data/app/themes/bagel-shop/templates/entries/pages.liquid +12 -0
  93. data/app/themes/bagel-shop/templates/entries/posts.liquid +19 -0
  94. data/app/themes/bagel-shop/templates/index.liquid +55 -0
  95. data/app/themes/bagel-shop/theme.yml +30 -0
  96. data/app/themes/default/assets/screenshot.svg +24 -0
  97. data/app/themes/default/assets/theme.css +89 -0
  98. data/app/themes/default/layouts/base.liquid +117 -0
  99. data/app/themes/default/templates/collections/_default.liquid +16 -0
  100. data/app/themes/default/templates/collections/posts.liquid +27 -0
  101. data/app/themes/default/templates/entries/_default.liquid +26 -0
  102. data/app/themes/default/templates/entries/landing.liquid +5 -0
  103. data/app/themes/default/templates/entries/pages.liquid +17 -0
  104. data/app/themes/default/templates/entries/posts.liquid +20 -0
  105. data/app/themes/default/templates/index.liquid +22 -0
  106. data/app/themes/default/templates/search.liquid +28 -0
  107. data/app/themes/default/templates/taxonomies/_default.liquid +25 -0
  108. data/app/themes/default/templates/taxonomies/_default_index.liquid +14 -0
  109. data/app/themes/default/theme.yml +23 -0
  110. data/app/views/layouts/plum/cp.html.erb +138 -0
  111. data/app/views/layouts/plum/session.html.erb +43 -0
  112. data/app/views/plum/cp/assets/_form.html.erb +44 -0
  113. data/app/views/plum/cp/assets/edit.html.erb +14 -0
  114. data/app/views/plum/cp/assets/index.html.erb +52 -0
  115. data/app/views/plum/cp/assets/new.html.erb +9 -0
  116. data/app/views/plum/cp/content_types/_form.html.erb +113 -0
  117. data/app/views/plum/cp/content_types/edit.html.erb +8 -0
  118. data/app/views/plum/cp/content_types/index.html.erb +48 -0
  119. data/app/views/plum/cp/content_types/new.html.erb +5 -0
  120. data/app/views/plum/cp/content_types/show.html.erb +59 -0
  121. data/app/views/plum/cp/dashboard/show.html.erb +50 -0
  122. data/app/views/plum/cp/entries/_form.html.erb +196 -0
  123. data/app/views/plum/cp/entries/edit.html.erb +9 -0
  124. data/app/views/plum/cp/entries/index.html.erb +51 -0
  125. data/app/views/plum/cp/entries/new.html.erb +5 -0
  126. data/app/views/plum/cp/form_definitions/_form.html.erb +90 -0
  127. data/app/views/plum/cp/form_definitions/edit.html.erb +11 -0
  128. data/app/views/plum/cp/form_definitions/index.html.erb +43 -0
  129. data/app/views/plum/cp/form_definitions/new.html.erb +5 -0
  130. data/app/views/plum/cp/form_definitions/show.html.erb +70 -0
  131. data/app/views/plum/cp/form_submissions/show.html.erb +20 -0
  132. data/app/views/plum/cp/globals/_form.html.erb +44 -0
  133. data/app/views/plum/cp/globals/edit.html.erb +11 -0
  134. data/app/views/plum/cp/globals/index.html.erb +41 -0
  135. data/app/views/plum/cp/globals/new.html.erb +5 -0
  136. data/app/views/plum/cp/globals/show.html.erb +14 -0
  137. data/app/views/plum/cp/nav_items/_form.html.erb +56 -0
  138. data/app/views/plum/cp/nav_items/edit.html.erb +11 -0
  139. data/app/views/plum/cp/nav_items/new.html.erb +6 -0
  140. data/app/views/plum/cp/nav_menus/_form.html.erb +33 -0
  141. data/app/views/plum/cp/nav_menus/_nav_item.html.erb +24 -0
  142. data/app/views/plum/cp/nav_menus/edit.html.erb +11 -0
  143. data/app/views/plum/cp/nav_menus/index.html.erb +41 -0
  144. data/app/views/plum/cp/nav_menus/new.html.erb +5 -0
  145. data/app/views/plum/cp/nav_menus/show.html.erb +24 -0
  146. data/app/views/plum/cp/shared/_icon.html.erb +39 -0
  147. data/app/views/plum/cp/shared/_image_picker.html.erb +38 -0
  148. data/app/views/plum/cp/site_settings/edit.html.erb +145 -0
  149. data/app/views/plum/cp/site_settings/show.html.erb +62 -0
  150. data/app/views/plum/cp/taxonomies/_form.html.erb +38 -0
  151. data/app/views/plum/cp/taxonomies/edit.html.erb +2 -0
  152. data/app/views/plum/cp/taxonomies/index.html.erb +38 -0
  153. data/app/views/plum/cp/taxonomies/new.html.erb +2 -0
  154. data/app/views/plum/cp/taxonomies/show.html.erb +46 -0
  155. data/app/views/plum/cp/terms/_form.html.erb +30 -0
  156. data/app/views/plum/cp/terms/edit.html.erb +2 -0
  157. data/app/views/plum/cp/terms/new.html.erb +2 -0
  158. data/app/views/plum/cp/themes/index.html.erb +160 -0
  159. data/app/views/plum/form_mailer/submission_notification.html.erb +13 -0
  160. data/app/views/plum/form_mailer/submission_notification.text.erb +7 -0
  161. data/app/views/plum/sessions/new.html.erb +20 -0
  162. data/compat/plum-cms.rb +1 -0
  163. data/config/locales/en.yml +31 -0
  164. data/config/plum_importmap.rb +4 -0
  165. data/config/plum_routes.rb +34 -0
  166. data/db/engine_migrate/20260101000001_create_plum_tables.rb +154 -0
  167. data/lib/generators/plum/install/install_generator.rb +54 -0
  168. data/lib/generators/plum/install/templates/plum_initializer.rb +37 -0
  169. data/lib/plum/configuration.rb +88 -0
  170. data/lib/plum/content_source.rb +45 -0
  171. data/lib/plum/content_source_context.rb +30 -0
  172. data/lib/plum/content_source_registry.rb +120 -0
  173. data/lib/plum/engine.rb +30 -0
  174. data/lib/plum/liquid_tags/form_tag.rb +17 -0
  175. data/lib/plum/version.rb +7 -0
  176. data/lib/plum.rb +11 -0
  177. data/vendor/javascript/lexxy.js +11859 -0
  178. metadata +405 -0
@@ -0,0 +1,51 @@
1
+ <div class="flex items-center justify-between mb-8">
2
+ <div>
3
+ <h1 class="text-2xl font-bold text-gray-900"><%= @content_type.name %></h1>
4
+ </div>
5
+ <%= link_to "New Entry", new_cp_content_type_entry_path(@content_type),
6
+ class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-purple-600 hover:bg-purple-700" %>
7
+ </div>
8
+
9
+ <% if @entries.any? %>
10
+ <div class="bg-white shadow rounded-lg overflow-hidden">
11
+ <table class="min-w-full divide-y divide-gray-200">
12
+ <thead class="bg-gray-50">
13
+ <tr>
14
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Title</th>
15
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Slug</th>
16
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
17
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Updated</th>
18
+ <th class="relative px-6 py-3"><span class="sr-only">Actions</span></th>
19
+ </tr>
20
+ </thead>
21
+ <tbody class="bg-white divide-y divide-gray-200">
22
+ <% @entries.each do |entry| %>
23
+ <tr>
24
+ <td class="px-6 py-4 whitespace-nowrap">
25
+ <%= link_to entry.title, edit_cp_content_type_entry_path(@content_type, entry), class: "text-sm font-medium text-purple-600 hover:text-purple-900" %>
26
+ </td>
27
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">/<%= entry.slug %></td>
28
+ <td class="px-6 py-4 whitespace-nowrap">
29
+ <span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium <%= entry.published? ? 'bg-green-100 text-green-800' : 'bg-yellow-100 text-yellow-800' %>">
30
+ <%= entry.status %>
31
+ </span>
32
+ </td>
33
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"><%= entry.updated_at.strftime("%b %d, %Y") %></td>
34
+ <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
35
+ <%= link_to "Edit", edit_cp_content_type_entry_path(@content_type, entry), class: "text-purple-600 hover:text-purple-900" %>
36
+ </td>
37
+ </tr>
38
+ <% end %>
39
+ </tbody>
40
+ </table>
41
+ </div>
42
+ <% else %>
43
+ <div class="text-center py-12 bg-white rounded-lg shadow">
44
+ <h3 class="mt-2 text-sm font-medium text-gray-900">No entries yet</h3>
45
+ <p class="mt-1 text-sm text-gray-500">Get started by creating your first entry.</p>
46
+ <div class="mt-6">
47
+ <%= link_to "New Entry", new_cp_content_type_entry_path(@content_type),
48
+ class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-purple-600 hover:bg-purple-700" %>
49
+ </div>
50
+ </div>
51
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <div class="mb-8">
2
+ <h1 class="text-2xl font-bold text-gray-900">New <%= @content_type.name.singularize %></h1>
3
+ </div>
4
+
5
+ <%= render "form", entry: @entry %>
@@ -0,0 +1,90 @@
1
+ <%= form_with model: [:cp, form_definition], class: "space-y-6" do |f| %>
2
+ <% if form_definition.errors.any? %>
3
+ <div class="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded">
4
+ <ul class="list-disc list-inside">
5
+ <% form_definition.errors.full_messages.each do |message| %>
6
+ <li><%= message %></li>
7
+ <% end %>
8
+ </ul>
9
+ </div>
10
+ <% end %>
11
+
12
+ <div class="bg-white shadow rounded-lg p-6 space-y-6">
13
+ <div>
14
+ <%= f.label :name, class: "block text-sm font-medium text-gray-700" %>
15
+ <%= f.text_field :name,
16
+ class: "mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500" %>
17
+ </div>
18
+
19
+ <div>
20
+ <%= f.label :handle, class: "block text-sm font-medium text-gray-700" %>
21
+ <%= f.text_field :handle,
22
+ class: "mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500 font-mono",
23
+ placeholder: "contact, quote_request, newsletter" %>
24
+ <p class="mt-1 text-sm text-gray-500">Used in Liquid as <span class="font-mono">{% form "<%= form_definition.handle.presence || "contact" %>" %}</span>.</p>
25
+ </div>
26
+
27
+ <div>
28
+ <%= f.label :notification_email, "Notification Email", class: "block text-sm font-medium text-gray-700" %>
29
+ <%= f.email_field :notification_email,
30
+ class: "mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500",
31
+ placeholder: "owner@example.com" %>
32
+ </div>
33
+ </div>
34
+
35
+ <div class="bg-white shadow rounded-lg p-6" data-controller="plum--form-fields">
36
+ <div class="flex items-center justify-between gap-4 mb-4">
37
+ <div>
38
+ <h2 class="text-lg font-medium text-gray-900">Fields</h2>
39
+ <p class="mt-1 text-sm text-gray-500">Text, email, textarea, select, and checkbox fields.</p>
40
+ </div>
41
+ <button type="button" data-action="plum--form-fields#addField"
42
+ class="px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50">
43
+ Add Field
44
+ </button>
45
+ </div>
46
+
47
+ <%= hidden_field_tag "form_definition[fields_json]",
48
+ form_fields_json(form_definition),
49
+ data: { "plum--form-fields-target": "input" } %>
50
+
51
+ <div class="space-y-4" data-plum--form-fields-target="fields">
52
+ <% form_definition.form_fields.each do |field| %>
53
+ <div class="grid grid-cols-1 gap-4 p-4 bg-gray-50 rounded-lg sm:grid-cols-2" data-plum--form-fields-target="field">
54
+ <input type="text" value="<%= field["handle"] %>" placeholder="handle" data-field="handle"
55
+ data-action="input->plum--form-fields#inputChanged"
56
+ class="px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500 font-mono text-sm">
57
+ <input type="text" value="<%= field["label"] %>" placeholder="Label" data-field="label"
58
+ data-action="input->plum--form-fields#inputChanged"
59
+ class="px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500 text-sm">
60
+ <select data-field="type" data-action="change->plum--form-fields#inputChanged"
61
+ class="px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500 text-sm">
62
+ <% Plum::FormDefinition::FIELD_TYPES.each do |type| %>
63
+ <option value="<%= type %>" <%= "selected" if field["type"] == type %>><%= type.humanize %></option>
64
+ <% end %>
65
+ </select>
66
+ <input type="text" value="<%= Array(field["options"]).join(", ") %>" placeholder="Options, comma-separated" data-field="options"
67
+ data-action="input->plum--form-fields#inputChanged"
68
+ class="px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500 text-sm">
69
+ <label class="flex items-center gap-2 text-sm text-gray-700">
70
+ <input type="checkbox" data-field="required" data-action="change->plum--form-fields#inputChanged"
71
+ <%= "checked" if ActiveModel::Type::Boolean.new.cast(field["required"]) %>
72
+ class="h-4 w-4 rounded border-gray-300 text-purple-600 focus:ring-purple-500">
73
+ Required
74
+ </label>
75
+ <button type="button" data-action="plum--form-fields#removeField"
76
+ class="justify-self-start text-sm font-medium text-red-700 hover:text-red-700">
77
+ Remove
78
+ </button>
79
+ </div>
80
+ <% end %>
81
+ </div>
82
+ </div>
83
+
84
+ <div class="flex justify-end space-x-3">
85
+ <%= link_to "Cancel", form_definition.persisted? ? cp_form_definition_path(form_definition) : cp_form_definitions_path,
86
+ class: "px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50" %>
87
+ <%= f.submit form_definition.persisted? ? "Save Form" : "Create Form",
88
+ class: "px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-purple-600 hover:bg-purple-700 cursor-pointer" %>
89
+ </div>
90
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <div class="flex items-center justify-between gap-6 mb-8">
2
+ <div>
3
+ <h1 class="text-2xl font-bold text-gray-900">Edit Form</h1>
4
+ <p class="mt-1 text-sm text-gray-500 font-mono"><%= @form_definition.handle %></p>
5
+ </div>
6
+ <%= button_to "Delete", cp_form_definition_path(@form_definition), method: :delete,
7
+ data: { turbo_confirm: "Delete this form and its submissions?" },
8
+ class: "px-4 py-2 border border-red-300 rounded-md shadow-sm text-sm font-medium text-red-700 bg-white hover:bg-red-50" %>
9
+ </div>
10
+
11
+ <%= render "form", form_definition: @form_definition %>
@@ -0,0 +1,43 @@
1
+ <div class="flex items-center justify-between gap-6 mb-8">
2
+ <div>
3
+ <h1 class="text-2xl font-bold text-gray-900">Forms</h1>
4
+ <p class="mt-1 text-sm text-gray-500">Public forms and submissions.</p>
5
+ </div>
6
+ <%= link_to "New Form", new_cp_form_definition_path,
7
+ class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-purple-600 hover:bg-purple-700" %>
8
+ </div>
9
+
10
+ <% if @form_definitions.any? %>
11
+ <div class="bg-white shadow rounded-lg overflow-hidden">
12
+ <table class="min-w-full divide-y divide-gray-200">
13
+ <thead class="bg-gray-50">
14
+ <tr>
15
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
16
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Handle</th>
17
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Fields</th>
18
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Submissions</th>
19
+ <th class="relative px-6 py-3"><span class="sr-only">Actions</span></th>
20
+ </tr>
21
+ </thead>
22
+ <tbody class="bg-white divide-y divide-gray-200">
23
+ <% @form_definitions.each do |form| %>
24
+ <tr>
25
+ <td class="px-6 py-4 whitespace-nowrap">
26
+ <%= link_to form.name, cp_form_definition_path(form), class: "text-sm font-medium text-purple-600 hover:text-purple-900" %>
27
+ </td>
28
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 font-mono"><%= form.handle %></td>
29
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"><%= form.form_fields.size %></td>
30
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"><%= form.form_submissions.size %></td>
31
+ <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
32
+ <%= link_to "Edit", edit_cp_form_definition_path(form), class: "text-purple-600 hover:text-purple-900" %>
33
+ </td>
34
+ </tr>
35
+ <% end %>
36
+ </tbody>
37
+ </table>
38
+ </div>
39
+ <% else %>
40
+ <div class="bg-white shadow rounded-lg p-6">
41
+ <p class="text-sm text-gray-500">No forms yet.</p>
42
+ </div>
43
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <div class="mb-8">
2
+ <h1 class="text-2xl font-bold text-gray-900">New Form</h1>
3
+ </div>
4
+
5
+ <%= render "form", form_definition: @form_definition %>
@@ -0,0 +1,70 @@
1
+ <div class="flex items-center justify-between gap-6 mb-8">
2
+ <div>
3
+ <h1 class="text-2xl font-bold text-gray-900"><%= @form_definition.name %></h1>
4
+ <p class="mt-1 text-sm text-gray-500">
5
+ Liquid: <span class="font-mono">{% form "<%= @form_definition.handle %>" %}</span>
6
+ </p>
7
+ </div>
8
+ <%= link_to "Edit Form", edit_cp_form_definition_path(@form_definition),
9
+ class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-purple-600 hover:bg-purple-700" %>
10
+ </div>
11
+
12
+ <div class="grid grid-cols-1 gap-6 lg:grid-cols-3">
13
+ <section class="lg:col-span-2">
14
+ <div class="bg-white shadow rounded-lg overflow-hidden">
15
+ <div class="px-6 py-4 border-b border-gray-200">
16
+ <h2 class="text-lg font-medium text-gray-900">Submissions</h2>
17
+ </div>
18
+
19
+ <% if @submissions.any? %>
20
+ <table class="min-w-full divide-y divide-gray-200">
21
+ <thead class="bg-gray-50">
22
+ <tr>
23
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Submitted</th>
24
+ <% @form_definition.form_fields.first(2).each do |field| %>
25
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"><%= field["label"].presence || field["handle"].to_s.humanize %></th>
26
+ <% end %>
27
+ <th class="relative px-6 py-3"><span class="sr-only">Actions</span></th>
28
+ </tr>
29
+ </thead>
30
+ <tbody class="bg-white divide-y divide-gray-200">
31
+ <% @submissions.each do |submission| %>
32
+ <tr>
33
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"><%= l(submission.created_at, format: :short) %></td>
34
+ <% @form_definition.form_fields.first(2).each do |field| %>
35
+ <td class="px-6 py-4 text-sm text-gray-700"><%= submission.value(field["handle"]) %></td>
36
+ <% end %>
37
+ <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
38
+ <%= link_to "View", cp_form_definition_form_submission_path(@form_definition, submission), class: "text-purple-600 hover:text-purple-900" %>
39
+ </td>
40
+ </tr>
41
+ <% end %>
42
+ </tbody>
43
+ </table>
44
+ <% else %>
45
+ <div class="p-6">
46
+ <p class="text-sm text-gray-500">No submissions yet.</p>
47
+ </div>
48
+ <% end %>
49
+ </div>
50
+ </section>
51
+
52
+ <aside class="bg-white shadow rounded-lg p-6 space-y-4">
53
+ <h2 class="text-lg font-medium text-gray-900">Fields</h2>
54
+ <% if @form_definition.form_fields.any? %>
55
+ <dl class="space-y-3">
56
+ <% @form_definition.form_fields.each do |field| %>
57
+ <div>
58
+ <dt class="text-sm font-medium text-gray-900"><%= field["label"].presence || field["handle"].to_s.humanize %></dt>
59
+ <dd class="mt-1 text-sm text-gray-500">
60
+ <span class="font-mono"><%= field["handle"] %></span> · <%= field["type"].to_s.humanize %>
61
+ <% if ActiveModel::Type::Boolean.new.cast(field["required"]) %> · Required<% end %>
62
+ </dd>
63
+ </div>
64
+ <% end %>
65
+ </dl>
66
+ <% else %>
67
+ <p class="text-sm text-gray-500">No fields configured.</p>
68
+ <% end %>
69
+ </aside>
70
+ </div>
@@ -0,0 +1,20 @@
1
+ <div class="flex items-center justify-between gap-6 mb-8">
2
+ <div>
3
+ <h1 class="text-2xl font-bold text-gray-900">Submission</h1>
4
+ <p class="mt-1 text-sm text-gray-500"><%= @form_definition.name %> · <%= l(@form_submission.created_at, format: :short) %></p>
5
+ </div>
6
+ <%= button_to "Delete", cp_form_definition_form_submission_path(@form_definition, @form_submission), method: :delete,
7
+ data: { turbo_confirm: "Delete this submission?" },
8
+ class: "px-4 py-2 border border-red-300 rounded-md shadow-sm text-sm font-medium text-red-700 bg-white hover:bg-red-50" %>
9
+ </div>
10
+
11
+ <div class="bg-white shadow rounded-lg p-6">
12
+ <dl class="divide-y divide-gray-200">
13
+ <% @form_definition.form_fields.each do |field| %>
14
+ <div class="py-4">
15
+ <dt class="text-sm font-medium text-gray-500"><%= field["label"].presence || field["handle"].to_s.humanize %></dt>
16
+ <dd class="mt-1 text-sm text-gray-900 whitespace-pre-wrap"><%= @form_submission.value(field["handle"]) %></dd>
17
+ </div>
18
+ <% end %>
19
+ </dl>
20
+ </div>
@@ -0,0 +1,44 @@
1
+ <%= form_with model: [:cp, global], class: "space-y-6" do |f| %>
2
+ <% if global.errors.any? %>
3
+ <div class="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded">
4
+ <ul class="list-disc list-inside">
5
+ <% global.errors.full_messages.each do |message| %>
6
+ <li><%= message %></li>
7
+ <% end %>
8
+ </ul>
9
+ </div>
10
+ <% end %>
11
+
12
+ <div class="bg-white shadow rounded-lg p-6 space-y-6">
13
+ <div>
14
+ <%= f.label :name, class: "block text-sm font-medium text-gray-700" %>
15
+ <%= f.text_field :name,
16
+ class: "mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500" %>
17
+ </div>
18
+
19
+ <div>
20
+ <%= f.label :handle, class: "block text-sm font-medium text-gray-700" %>
21
+ <%= f.text_field :handle,
22
+ class: "mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500 font-mono",
23
+ placeholder: "auto-generated from name" %>
24
+ <p class="mt-1 text-sm text-gray-500">Used in Liquid as <span class="font-mono">globals.company.phone</span>.</p>
25
+ </div>
26
+
27
+ <div>
28
+ <%= f.label :data_json, "Data", class: "block text-sm font-medium text-gray-700" %>
29
+ <%= f.text_area :data_json,
30
+ name: "global[data_json]",
31
+ value: global_data_json(global),
32
+ rows: 14,
33
+ class: "mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500 font-mono text-sm",
34
+ spellcheck: false %>
35
+ <p class="mt-1 text-sm text-gray-500">Must be a JSON object.</p>
36
+ </div>
37
+ </div>
38
+
39
+ <div class="flex justify-end space-x-3">
40
+ <%= link_to "Cancel", global.persisted? ? cp_global_path(global) : cp_globals_path,
41
+ class: "px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50" %>
42
+ <%= f.submit class: "px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-purple-600 hover:bg-purple-700 cursor-pointer" %>
43
+ </div>
44
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <div class="flex items-center justify-between gap-6 mb-8">
2
+ <div>
3
+ <h1 class="text-2xl font-bold text-gray-900">Edit Global</h1>
4
+ <p class="mt-1 text-sm text-gray-500 font-mono"><%= @global.handle %></p>
5
+ </div>
6
+ <%= button_to "Delete", cp_global_path(@global), method: :delete,
7
+ data: { turbo_confirm: "Delete this global?" },
8
+ class: "px-4 py-2 border border-red-300 rounded-md shadow-sm text-sm font-medium text-red-700 bg-white hover:bg-red-50" %>
9
+ </div>
10
+
11
+ <%= render "form", global: @global %>
@@ -0,0 +1,41 @@
1
+ <div class="flex items-center justify-between gap-6 mb-8">
2
+ <div>
3
+ <h1 class="text-2xl font-bold text-gray-900">Globals</h1>
4
+ <p class="mt-1 text-sm text-gray-500">Reusable site data for Liquid templates.</p>
5
+ </div>
6
+ <%= link_to "New Global", new_cp_global_path,
7
+ class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-purple-600 hover:bg-purple-700" %>
8
+ </div>
9
+
10
+ <% if @globals.any? %>
11
+ <div class="bg-white shadow rounded-lg overflow-hidden">
12
+ <table class="min-w-full divide-y divide-gray-200">
13
+ <thead class="bg-gray-50">
14
+ <tr>
15
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
16
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Handle</th>
17
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Keys</th>
18
+ <th class="relative px-6 py-3"><span class="sr-only">Actions</span></th>
19
+ </tr>
20
+ </thead>
21
+ <tbody class="bg-white divide-y divide-gray-200">
22
+ <% @globals.each do |global| %>
23
+ <tr>
24
+ <td class="px-6 py-4 whitespace-nowrap">
25
+ <%= link_to global.name, cp_global_path(global), class: "text-sm font-medium text-purple-600 hover:text-purple-900" %>
26
+ </td>
27
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 font-mono"><%= global.handle %></td>
28
+ <td class="px-6 py-4 text-sm text-gray-500"><%= global.data.keys.to_sentence.presence || "No keys" %></td>
29
+ <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
30
+ <%= link_to "Edit", edit_cp_global_path(global), class: "text-purple-600 hover:text-purple-900" %>
31
+ </td>
32
+ </tr>
33
+ <% end %>
34
+ </tbody>
35
+ </table>
36
+ </div>
37
+ <% else %>
38
+ <div class="bg-white shadow rounded-lg p-6">
39
+ <p class="text-sm text-gray-500">No globals yet.</p>
40
+ </div>
41
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <div class="mb-8">
2
+ <h1 class="text-2xl font-bold text-gray-900">New Global</h1>
3
+ </div>
4
+
5
+ <%= render "form", global: @global %>
@@ -0,0 +1,14 @@
1
+ <div class="flex items-center justify-between gap-6 mb-8">
2
+ <div>
3
+ <h1 class="text-2xl font-bold text-gray-900"><%= @global.name %></h1>
4
+ <p class="mt-1 text-sm text-gray-500">
5
+ Liquid: <span class="font-mono">globals.<%= @global.handle %></span>
6
+ </p>
7
+ </div>
8
+ <%= link_to "Edit", edit_cp_global_path(@global),
9
+ class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-purple-600 hover:bg-purple-700" %>
10
+ </div>
11
+
12
+ <div class="bg-white shadow rounded-lg p-6">
13
+ <pre class="overflow-x-auto whitespace-pre-wrap text-sm text-gray-800"><%= JSON.pretty_generate(@global.data) %></pre>
14
+ </div>
@@ -0,0 +1,56 @@
1
+ <%= form_with model: [:cp, @nav_menu, nav_item], class: "space-y-6" do |f| %>
2
+ <% if nav_item.errors.any? %>
3
+ <div class="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded">
4
+ <ul class="list-disc list-inside">
5
+ <% nav_item.errors.full_messages.each do |message| %>
6
+ <li><%= message %></li>
7
+ <% end %>
8
+ </ul>
9
+ </div>
10
+ <% end %>
11
+
12
+ <div class="bg-white shadow rounded-lg p-6 space-y-6">
13
+ <div>
14
+ <%= f.label :label, class: "block text-sm font-medium text-gray-700" %>
15
+ <%= f.text_field :label,
16
+ class: "mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500" %>
17
+ </div>
18
+
19
+ <div>
20
+ <%= f.label :url, "Custom URL", class: "block text-sm font-medium text-gray-700" %>
21
+ <%= f.text_field :url,
22
+ class: "mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500 font-mono",
23
+ placeholder: "/menu or https://example.com" %>
24
+ <p class="mt-1 text-sm text-gray-500">Leave blank when linking to an entry.</p>
25
+ </div>
26
+
27
+ <div>
28
+ <%= f.label :entry_id, "Entry", class: "block text-sm font-medium text-gray-700" %>
29
+ <%= f.select :entry_id,
30
+ options_for_select([["No entry", ""]] + @entries.map { |entry| ["#{entry.title} (#{entry.content_type.name})", entry.id] }, nav_item.entry_id),
31
+ {},
32
+ class: "mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500" %>
33
+ </div>
34
+
35
+ <div>
36
+ <%= f.label :parent_id, "Parent Item", class: "block text-sm font-medium text-gray-700" %>
37
+ <%= f.select :parent_id,
38
+ options_for_select([["No parent", ""]] + @parent_options.map { |item| [item.label, item.id] }, nav_item.parent_id),
39
+ {},
40
+ class: "mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500" %>
41
+ </div>
42
+
43
+ <div>
44
+ <%= f.label :position, class: "block text-sm font-medium text-gray-700" %>
45
+ <%= f.number_field :position,
46
+ min: 0,
47
+ class: "mt-1 block w-32 px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500" %>
48
+ </div>
49
+ </div>
50
+
51
+ <div class="flex justify-end space-x-3">
52
+ <%= link_to "Cancel", cp_nav_menu_path(@nav_menu),
53
+ class: "px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50" %>
54
+ <%= f.submit class: "px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-purple-600 hover:bg-purple-700 cursor-pointer" %>
55
+ </div>
56
+ <% end %>
@@ -0,0 +1,11 @@
1
+ <div class="flex items-center justify-between gap-6 mb-8">
2
+ <div>
3
+ <h1 class="text-2xl font-bold text-gray-900">Edit Navigation Item</h1>
4
+ <p class="mt-1 text-sm text-gray-500"><%= @nav_menu.name %></p>
5
+ </div>
6
+ <%= button_to "Delete", cp_nav_menu_nav_item_path(@nav_menu, @nav_item), method: :delete,
7
+ data: { turbo_confirm: "Delete this navigation item?" },
8
+ class: "px-4 py-2 border border-red-300 rounded-md shadow-sm text-sm font-medium text-red-700 bg-white hover:bg-red-50" %>
9
+ </div>
10
+
11
+ <%= render "form", nav_item: @nav_item %>
@@ -0,0 +1,6 @@
1
+ <div class="mb-8">
2
+ <h1 class="text-2xl font-bold text-gray-900">New Navigation Item</h1>
3
+ <p class="mt-1 text-sm text-gray-500"><%= @nav_menu.name %></p>
4
+ </div>
5
+
6
+ <%= render "form", nav_item: @nav_item %>
@@ -0,0 +1,33 @@
1
+ <%= form_with model: [:cp, nav_menu], class: "space-y-6" do |f| %>
2
+ <% if nav_menu.errors.any? %>
3
+ <div class="bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded">
4
+ <ul class="list-disc list-inside">
5
+ <% nav_menu.errors.full_messages.each do |message| %>
6
+ <li><%= message %></li>
7
+ <% end %>
8
+ </ul>
9
+ </div>
10
+ <% end %>
11
+
12
+ <div class="bg-white shadow rounded-lg p-6 space-y-6">
13
+ <div>
14
+ <%= f.label :name, class: "block text-sm font-medium text-gray-700" %>
15
+ <%= f.text_field :name,
16
+ class: "mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500" %>
17
+ </div>
18
+
19
+ <div>
20
+ <%= f.label :handle, class: "block text-sm font-medium text-gray-700" %>
21
+ <%= f.text_field :handle,
22
+ class: "mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-purple-500 focus:border-purple-500 font-mono",
23
+ placeholder: "main, footer, legal" %>
24
+ <p class="mt-1 text-sm text-gray-500">Used in Liquid as <span class="font-mono">nav.main.items</span>.</p>
25
+ </div>
26
+ </div>
27
+
28
+ <div class="flex justify-end space-x-3">
29
+ <%= link_to "Cancel", nav_menu.persisted? ? cp_nav_menu_path(nav_menu) : cp_nav_menus_path,
30
+ class: "px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50" %>
31
+ <%= f.submit class: "px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-purple-600 hover:bg-purple-700 cursor-pointer" %>
32
+ </div>
33
+ <% end %>
@@ -0,0 +1,24 @@
1
+ <li class="bg-white shadow rounded-lg p-4">
2
+ <div class="flex items-start justify-between gap-4">
3
+ <div class="min-w-0">
4
+ <h2 class="text-sm font-semibold text-gray-900"><%= item.label %></h2>
5
+ <p class="mt-1 text-sm text-gray-500 truncate">
6
+ <% if item.entry %>
7
+ Entry: <%= item.entry.title %> <span class="font-mono">/<%= item.entry.slug %></span>
8
+ <% else %>
9
+ <span class="font-mono"><%= item.url %></span>
10
+ <% end %>
11
+ </p>
12
+ </div>
13
+ <div class="flex shrink-0 items-center gap-3">
14
+ <span class="text-xs text-gray-500">#<%= item.position.presence || 0 %></span>
15
+ <%= link_to "Edit", edit_cp_nav_menu_nav_item_path(nav_menu, item), class: "text-sm font-medium text-purple-600 hover:text-purple-900" %>
16
+ </div>
17
+ </div>
18
+
19
+ <% if item.children.any? %>
20
+ <ol class="mt-3 pl-5 space-y-3 border-l border-gray-200">
21
+ <%= render partial: "nav_item", collection: item.children, as: :item, locals: { nav_menu: nav_menu } %>
22
+ </ol>
23
+ <% end %>
24
+ </li>
@@ -0,0 +1,11 @@
1
+ <div class="flex items-center justify-between gap-6 mb-8">
2
+ <div>
3
+ <h1 class="text-2xl font-bold text-gray-900">Edit Navigation Menu</h1>
4
+ <p class="mt-1 text-sm text-gray-500 font-mono"><%= @nav_menu.handle %></p>
5
+ </div>
6
+ <%= button_to "Delete", cp_nav_menu_path(@nav_menu), method: :delete,
7
+ data: { turbo_confirm: "Delete this navigation menu?" },
8
+ class: "px-4 py-2 border border-red-300 rounded-md shadow-sm text-sm font-medium text-red-700 bg-white hover:bg-red-50" %>
9
+ </div>
10
+
11
+ <%= render "form", nav_menu: @nav_menu %>
@@ -0,0 +1,41 @@
1
+ <div class="flex items-center justify-between gap-6 mb-8">
2
+ <div>
3
+ <h1 class="text-2xl font-bold text-gray-900">Navigation</h1>
4
+ <p class="mt-1 text-sm text-gray-500">Menus available to Liquid templates.</p>
5
+ </div>
6
+ <%= link_to "New Menu", new_cp_nav_menu_path,
7
+ class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-purple-600 hover:bg-purple-700" %>
8
+ </div>
9
+
10
+ <% if @nav_menus.any? %>
11
+ <div class="bg-white shadow rounded-lg overflow-hidden">
12
+ <table class="min-w-full divide-y divide-gray-200">
13
+ <thead class="bg-gray-50">
14
+ <tr>
15
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Name</th>
16
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Handle</th>
17
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Items</th>
18
+ <th class="relative px-6 py-3"><span class="sr-only">Actions</span></th>
19
+ </tr>
20
+ </thead>
21
+ <tbody class="bg-white divide-y divide-gray-200">
22
+ <% @nav_menus.each do |menu| %>
23
+ <tr>
24
+ <td class="px-6 py-4 whitespace-nowrap">
25
+ <%= link_to menu.name, cp_nav_menu_path(menu), class: "text-sm font-medium text-purple-600 hover:text-purple-900" %>
26
+ </td>
27
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500 font-mono"><%= menu.handle %></td>
28
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500"><%= menu.nav_items.size %></td>
29
+ <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
30
+ <%= link_to "Edit", edit_cp_nav_menu_path(menu), class: "text-purple-600 hover:text-purple-900" %>
31
+ </td>
32
+ </tr>
33
+ <% end %>
34
+ </tbody>
35
+ </table>
36
+ </div>
37
+ <% else %>
38
+ <div class="bg-white shadow rounded-lg p-6">
39
+ <p class="text-sm text-gray-500">No navigation menus yet.</p>
40
+ </div>
41
+ <% end %>
@@ -0,0 +1,5 @@
1
+ <div class="mb-8">
2
+ <h1 class="text-2xl font-bold text-gray-900">New Navigation Menu</h1>
3
+ </div>
4
+
5
+ <%= render "form", nav_menu: @nav_menu %>