diff options
| author | erdgeist <erdgeist@erdgeist.org> | 2026-07-06 16:49:45 +0200 |
|---|---|---|
| committer | erdgeist <erdgeist@erdgeist.org> | 2026-07-06 16:49:45 +0200 |
| commit | 36b5324900968afc7498950c14ad13be5c061a7c (patch) | |
| tree | a717b115e308a940cc69d2b979ab03f90e5aeb80 /app | |
| parent | 889e15eabbe107d2642fdd8aa3f03821058c00dc (diff) | |
Restructure nodes#show and nodes#new around node_description/node_content
nodes#show's flat <table> of label/value pairs grew unreadable once
Admin Preview and Public Preview were added alongside Public Link -
switched to the div.node_description/div.node_content pattern already
used by nodes#edit, grouped into People/Dates/Links/Revisions/Tags/
Events/Children sections rather than one undifferentiated list.
People/Dates/Links use a flex-based item layout, not a second <table> -
multiple independent tables sharing one outer width but different
column counts produced misaligned columns with no shared grid. Flex
items size to their own content with a shared minimum instead.
Revisions and Children are both collapsed via <details>, no JS needed -
previously nodes#show didn't list a node's children at all, blocking
the ability to find/navigate erfa and chaostreff pages without already
knowing they're now standalone nodes. Revision list items link to
node_revision_path(@node, page) - the actual per-revision diff/restore
view, not the plain index.
nodes#new converted to the same pattern for visual consistency, plus a
few things surfaced along the way: submit buttons were unstyled
site-wide (a bare input[type=submit] with no border, fill, or hover
state - Create on this exact page was easy to miss entirely), fixed
with a bordered/bold treatment reusing the existing form.button_to
visual language rather than introducing a fourth button style. Title
regained the bold weight it had before the table rewrite dropped it
silently. Tag list grouping and full link/button semantic taxonomy
(show vs edit/add vs publish/revoke vs destroy, applied consistently
across every link on the page, not just buttons) are known follow-ups,
not attempted here.
Diffstat (limited to 'app')
| -rw-r--r-- | app/views/nodes/new.html.erb | 55 | ||||
| -rw-r--r-- | app/views/nodes/show.html.erb | 204 |
2 files changed, 144 insertions, 115 deletions
diff --git a/app/views/nodes/new.html.erb b/app/views/nodes/new.html.erb index f557454..0a05325 100644 --- a/app/views/nodes/new.html.erb +++ b/app/views/nodes/new.html.erb | |||
| @@ -6,13 +6,11 @@ | |||
| 6 | </div> | 6 | </div> |
| 7 | <% end %> | 7 | <% end %> |
| 8 | 8 | ||
| 9 | <p>What kind of node do you want to create?</p> | ||
| 10 | |||
| 11 | <%= form_tag nodes_path do %> | 9 | <%= form_tag nodes_path do %> |
| 12 | <table id="new_node"> | 10 | <div id="new_node"> |
| 13 | <tr> | 11 | |
| 14 | <td class="description">Type</td> | 12 | <div class="node_description">Type</div> |
| 15 | <td> | 13 | <div class="node_content"> |
| 16 | <% CccConventions::NODE_KINDS.each do |kind, config| %> | 14 | <% CccConventions::NODE_KINDS.each do |kind, config| %> |
| 17 | <p> | 15 | <p> |
| 18 | <%= radio_button_tag :kind, kind, kind == "generic", | 16 | <%= radio_button_tag :kind, kind, kind == "generic", |
| @@ -23,35 +21,32 @@ | |||
| 23 | <% end %> | 21 | <% end %> |
| 24 | </p> | 22 | </p> |
| 25 | <% end %> | 23 | <% end %> |
| 26 | </td> | 24 | </div> |
| 27 | </tr> | 25 | |
| 28 | <tr> | 26 | <div class="node_description">Title</div> |
| 29 | <td class="description">Title</td> | 27 | <div class="node_content"> |
| 30 | <td><%= text_field_tag :title %> | 28 | <%= text_field_tag :title %> |
| 31 | <span class="field_hint">A URL slug will be generated from this automatically. You can review or adjust it afterward under the "metadata" section's Slug field.</span> | 29 | <span class="field_hint">A URL slug will be generated from this automatically. You can review or adjust it afterward under the "metadata" section's Slug field.</span> |
| 32 | </td> | 30 | </div> |
| 33 | </tr> | 31 | |
| 34 | <tr id="parent_search_field"> | 32 | <div id="parent_search_field"> |
| 35 | <td class="description">Parent</td> | 33 | <div class="node_description">Parent</div> |
| 36 | <td> | 34 | <div class="node_content"> |
| 37 | <%= text_field_tag :parent_search_term, @parent_name %> | 35 | <%= text_field_tag :parent_search_term, @parent_name %> |
| 38 | <%= hidden_field_tag :parent_id, @parent_id %> | 36 | <%= hidden_field_tag :parent_id, @parent_id %> |
| 39 | <div id="search_results"> | 37 | <div id="search_results"></div> |
| 38 | </div> | ||
| 39 | </div> | ||
| 40 | 40 | ||
| 41 | </div> | 41 | <div class="node_description">Resulting path</div> |
| 42 | </td> | 42 | <div class="node_content"> |
| 43 | </tr> | ||
| 44 | <tr> | ||
| 45 | <td class="description">Resulting path</td> | ||
| 46 | <td> | ||
| 47 | <span id="resulting_path">—</span> | 43 | <span id="resulting_path">—</span> |
| 48 | <button type="button" id="copy_resulting_path" class="unselected">copy</button> | 44 | <button type="button" id="copy_resulting_path" class="unselected">copy</button> |
| 49 | <span class="field_hint">This preview updates as you type. The final path can still be changed afterward by editing the title (for the last segment) or moving the node to a different parent (for everything before it).</span> | 45 | <span class="field_hint">This preview updates as you type. The final path can still be changed afterward by editing the title (for the last segment) or moving the node to a different parent (for everything before it).</span> |
| 50 | </td> | 46 | </div> |
| 51 | </tr> | 47 | |
| 52 | <tr> | 48 | <div class="node_description"></div> |
| 53 | <td></td> | 49 | <div class="node_content right"><%= submit_tag "Create" %></div> |
| 54 | <td class="right"><%= submit_tag "Create" %></td> | 50 | |
| 55 | </tr> | 51 | </div> |
| 56 | </table> | ||
| 57 | <% end %> | 52 | <% end %> |
diff --git a/app/views/nodes/show.html.erb b/app/views/nodes/show.html.erb index d63df86..189adb8 100644 --- a/app/views/nodes/show.html.erb +++ b/app/views/nodes/show.html.erb | |||
| @@ -1,95 +1,129 @@ | |||
| 1 | <% content_for :subnavigation do %> | 1 | <% content_for :subnavigation do %> |
| 2 | <%= link_to 'Edit', edit_node_path(@node), :class => "unselected" %> | 2 | <%= link_to 'Edit', edit_node_path(@node), :class => "unselected" %> |
| 3 | <%= link_to 'Preview', preview_page_path(@page) %> | 3 | <%= link_to 'Preview', preview_page_path(@page) %> |
| 4 | <%= link_to 'Revisions', node_revisions_path(@node) %> | ||
| 5 | <%= unlock_link if @node.locked? %> | ||
| 6 | <% end %> | 4 | <% end %> |
| 7 | 5 | ||
| 8 | |||
| 9 | <div id="page_editor" class="show_node"> | 6 | <div id="page_editor" class="show_node"> |
| 10 | <table id="content"> | 7 | <div id="content"> |
| 11 | <tr> | 8 | |
| 12 | <td class="description">Public Link</td> | 9 | <div class="node_description">People</div> |
| 13 | <td><%= link_to @page.public_link, content_url(@node.unique_path) %></td> | 10 | <div class="node_content node_info_group"> |
| 14 | </tr> | 11 | <div class="node_info_group_items"> |
| 15 | <% if @node.draft %> | 12 | <div class="node_info_item"> |
| 16 | <tr> | 13 | <span class="node_info_label">Author</span> |
| 17 | <td class="description">Admin Preview</td> | 14 | <%= @page.user.try(:login) %> |
| 18 | <td><%= link_to preview_page_path(@node.draft), preview_page_path(@node.draft) %></td> | 15 | </div> |
| 19 | </tr> | 16 | <div class="node_info_item"> |
| 20 | <tr> | 17 | <span class="node_info_label">Editor</span> |
| 21 | <td class="description">Public Preview</td> | 18 | <%= @page.editor.try(:login) %> |
| 22 | <td> | 19 | </div> |
| 23 | <% if @node.draft.preview_token.present? %> | 20 | <% if @node.locked? %> |
| 24 | <%= link_to shared_preview_path(token: @node.draft.preview_token), shared_preview_url(token: @node.draft.preview_token), target: "_blank", rel: "noopener" %> | 21 | <div class="node_info_item"> |
| 25 | <%= button_to 'Revoke', revoke_shared_preview_node_path(@node), method: :put, form: { data: { confirm: "Revoke this preview link? Anyone currently using it will immediately lose access." } } %> | 22 | <span class="node_info_label">Locked by</span> |
| 26 | <% else %> | 23 | <span class="warning"><%= @node.lock_owner.login %></span> |
| 27 | <%= button_to 'Create public preview link', generate_shared_preview_node_path(@node), method: :put %> | 24 | <%= unlock_link %> |
| 25 | </div> | ||
| 28 | <% end %> | 26 | <% end %> |
| 29 | </td> | 27 | </div> |
| 30 | </tr> | 28 | </div> |
| 31 | <% end %> | 29 | |
| 32 | <tr> | 30 | <div class="node_description">Dates</div> |
| 33 | <td class="description">Author</td> | 31 | <div class="node_content node_info_group"> |
| 34 | <td><%= @page.user.try(:login) %></td> | 32 | <div class="node_info_group_items"> |
| 35 | </tr> | 33 | <div class="node_info_item"> |
| 36 | <tr> | 34 | <span class="node_info_label">Last updated</span> |
| 37 | <td class="description">Editor</td> | 35 | <%= @page.updated_at %> |
| 38 | <td><%= @page.editor.try(:login) %></td> | 36 | </div> |
| 39 | </tr> | 37 | <% if @page.published_at.present? %> |
| 40 | <% if @page.node.locked? %> | 38 | <div class="node_info_item"> |
| 41 | <tr> | 39 | <span class="node_info_label"><%= @page.public? ? 'Published at' : 'Will publish at' %></span> |
| 42 | <td class="description">Locked by</td> | 40 | <%= @page.published_at %> |
| 43 | <td><span class="warning"><%= @page.node.lock_owner.login %></span></td> | 41 | </div> |
| 44 | </tr> | 42 | <% end %> |
| 45 | <% end %> | 43 | </div> |
| 46 | <tr> | 44 | </div> |
| 47 | <td class="description">Last updated</td> | 45 | |
| 48 | <td><%= @page.updated_at %></td> | 46 | <div class="node_description">Links</div> |
| 49 | </tr> | 47 | <div class="node_content node_info_group"> |
| 50 | <tr> | 48 | <div class="node_info_group_items"> |
| 51 | <td class="description">Published at</td> | 49 | <div class="node_info_item"> |
| 52 | <td><%= @page.published_at %></td> | 50 | <span class="node_info_label">Public</span> |
| 53 | </tr> | 51 | <%= link_to @page.public_link, content_url(@node.unique_path) %> |
| 54 | <tr> | 52 | </div> |
| 55 | <td class="description">Revision</td> | 53 | <% if @node.draft %> |
| 56 | <td><%= @page.revision %></td> | 54 | <div class="node_info_item"> |
| 57 | </tr> | 55 | <span class="node_info_label">Admin Preview</span> |
| 58 | <tr> | 56 | <%= link_to preview_page_path(@node.draft), preview_page_path(@node.draft) %> |
| 59 | <td class="description">Tagged with:</td> | 57 | </div> |
| 60 | <td><%= @page.tag_list %></td> | 58 | <div class="node_info_item"> |
| 61 | </tr> | 59 | <span class="node_info_label">Public Preview</span> |
| 62 | <tr> | 60 | <% if @node.draft.preview_token.present? %> |
| 63 | <td class="description">Events</td> | 61 | <%= link_to shared_preview_path(token: @node.draft.preview_token), shared_preview_url(token: @node.draft.preview_token), target: "_blank", rel: "noopener" %> |
| 64 | <td> | 62 | <%= button_to 'Revoke', revoke_shared_preview_node_path(@node), method: :put, form: { data: { confirm: "Revoke this preview link? Anyone currently using it will immediately lose access." } } %> |
| 65 | <ul> | 63 | <% else %> |
| 66 | <% @node.events.order(:start_time).each do |event| %> | 64 | <%= button_to 'Create public preview link', generate_shared_preview_node_path(@node), method: :put %> |
| 67 | <li> | 65 | <% end %> |
| 68 | <%= event_schedule_text(event) %> | 66 | </div> |
| 69 | [<%= link_to 'show', event_path(event, return_to: request.path) %> | ||
| 70 | | <%= link_to 'edit', edit_event_path(event, return_to: request.path) %>] | ||
| 71 | </li> | ||
| 72 | <% end %> | 67 | <% end %> |
| 68 | </div> | ||
| 69 | </div> | ||
| 70 | |||
| 71 | <div class="node_description">Revisions</div> | ||
| 72 | <div class="node_content node_info_group"> | ||
| 73 | <details> | ||
| 74 | <summary> | ||
| 75 | <%= pluralize(@node.pages.count, 'revision', 'revisions') %> | ||
| 76 | · <%= link_to 'full history (diff / restore)', node_revisions_path(@node) %> | ||
| 77 | </summary> | ||
| 78 | <ul> | ||
| 79 | <% @node.pages.order(:revision).each do |page| %> | ||
| 80 | <li><%= link_to "##{page.revision} — #{page.title} (#{page.user.try(:login)}, #{page.updated_at})", node_revision_path(@node, page) %></li> | ||
| 81 | <% end %> | ||
| 73 | </ul> | 82 | </ul> |
| 74 | <% mapping = default_event_tag_mapping(@page) %> | 83 | </details> |
| 75 | <%= link_to 'add event', new_event_path(node_id: @node.id, tag_list: mapping&.last, auto_tag_source: mapping&.first, return_to: request.path) %> | 84 | </div> |
| 76 | </td> | 85 | |
| 77 | </tr> | 86 | <div class="node_description">Tags</div> |
| 78 | <tr> | 87 | <div class="node_content node_info_group"><%= @page.tag_list %></div> |
| 79 | <td class="description"><strong>Title</strong></td> | 88 | |
| 80 | <td><%= sanitize( @page.title ) %></td> | 89 | <div class="node_description">Events</div> |
| 81 | </tr> | 90 | <div class="node_content node_info_group"> |
| 82 | <tr> | 91 | <ul> |
| 83 | <td class="description"><strong>Abstract</strong></td> | 92 | <% @node.events.order(:start_time).each do |event| %> |
| 84 | <td><%= sanitize( @page.abstract ) %></td> | 93 | <li> |
| 85 | </tr> | 94 | <%= event_schedule_text(event) %> |
| 86 | <tr> | 95 | [<%= link_to 'show', event_path(event, return_to: request.path) %> |
| 87 | <td class="description"><strong>Body</strong></td> | 96 | | <%= link_to 'edit', edit_event_path(event, return_to: request.path) %>] |
| 88 | <td><%= sanitize( @page.body ) %></td> | 97 | </li> |
| 89 | </tr> | 98 | <% end %> |
| 90 | <tr> | 99 | </ul> |
| 91 | <td></td> | 100 | <% mapping = default_event_tag_mapping(@page) %> |
| 92 | <td class="right"></td> | 101 | <%= link_to 'add event', new_event_path(node_id: @node.id, tag_list: mapping&.last, auto_tag_source: mapping&.first, return_to: request.path) %> |
| 93 | </tr> | 102 | </div> |
| 94 | </table> | 103 | |
| 104 | <% if @node.children.any? %> | ||
| 105 | <div class="node_description">Children</div> | ||
| 106 | <div class="node_content node_info_group"> | ||
| 107 | <details> | ||
| 108 | <summary><%= pluralize(@node.children.count, 'child', 'children') %></summary> | ||
| 109 | <ul> | ||
| 110 | <% @node.children.order(:slug).each do |child| %> | ||
| 111 | <li><%= link_to (child.head&.title || child.draft&.title || child.slug), node_path(child) %></li> | ||
| 112 | <% end %> | ||
| 113 | </ul> | ||
| 114 | </details> | ||
| 115 | </div> | ||
| 116 | <% end %> | ||
| 117 | |||
| 118 | <div class="node_description"><strong>Title</strong></div> | ||
| 119 | <div class="node_content"><strong><%= sanitize(@page.title) %></strong></div> | ||
| 120 | |||
| 121 | <div class="node_description">Abstract</div> | ||
| 122 | <div class="node_content"><%= sanitize(@page.abstract) %></div> | ||
| 123 | |||
| 124 | <div class="node_description">Body</div> | ||
| 125 | <div class="node_content"><%= sanitize(@page.body) %></div> | ||
| 126 | <%# Assets not yet addressed - no confirmed model/association seen for this page's attached assets %> | ||
| 127 | |||
| 128 | </div> | ||
| 95 | </div> | 129 | </div> |
