" data-plan-id="<%= plan.id %>" - data-status="<%= plan.status %>" - <% if author %> - draggable="true" - data-move-url="<%= move_to_folder_plan_path(plan) %>" - data-action="dragstart->coplan--folder-dnd#dragStart dragend->coplan--folder-dnd#dragEnd" - <% end %>> - - <%= link_to plan.title, plan_path(plan), class: "plan-row__title", data: { turbo_frame: "_top" } %> - <%= plan.status %> - <% if plan.folder %> - <%= link_to plans_path(params.permit(:scope, :status, :plan_type, :tag).merge(folder: plan.folder_id)), - class: "plan-row__folder", data: { turbo_frame: "_top" } do %> - - <%= folder_path_for(plan.folder) %> + data-plan-url="<%= plan_path(plan) %>" + data-visibility="<%= plan.visibility %>" + data-coplan--workspace-keys-target="item" + draggable="true" + data-move-url="<%= move_to_folder_plan_path(plan) %>" + data-current-folder-id="<%= my_folder_id %>" + data-action="dragstart->coplan--folder-dnd#dragStart dragend->coplan--folder-dnd#dragEnd"> + <%# The file icon spans both text rows — a Drive-style file entry, not an + inline glyph. %> + <%= plan_type_icon(plan, size: :lg) %> + + + <%= link_to plan.title, plan_path(plan), class: "plan-row__title", draggable: false, data: { turbo_frame: "_top" } %><%= plan_state_badge(plan) %> + <% if my_folder_id && my_folder_id != local_assigns[:suppress_folder_id] %> + <%= link_to workspace_path(folder: my_folder_id), + class: "plan-row__folder", draggable: false, data: { turbo_frame: "_top" } do %> + + <%= folder_paths_by_id[my_folder_id] %> + <% end %> <% end %> - <% end %> - <% plan.tags.each do |tag| %> - <%= link_to tag.name, - plans_path(params.permit(:scope, :status, :plan_type, :folder).merge(tag: tag.name)), - class: "badge badge--tag #{'badge--tag-active' if params[:tag] == tag.name}", - data: { turbo_frame: "_top" } %> - <% end %> - - <% if unread > 0 %> - "><%= unread %> - <% end %> - <%= time_ago_in_words(plan.updated_at) %> ago - <%= user_avatar(plan.created_by_user) %> - <% if author %> - - ⋯ - - <%# turbo_frame: _top — rows render inside pagination frames, but the - move redirect (and its flash) must replace the whole page. %> - <%= form_with url: move_to_folder_plan_path(plan), method: :patch, - class: "plan-row__move-form", data: { turbo_frame: "_top" } do |f| %> - Move to folder - <%= f.select :folder_id, - options_for_select([["No folder", ""]] + folder_select_options.map { |path, id, _| [path, id] }, plan.folder_id), - {}, class: "plan-row__move-select", id: "move-folder-#{plan.id}" %> - <%= f.submit "Move", class: "btn btn--sm" %> - <% end %> - - + <% plan.tags.each do |tag| %> + <%= link_to tag.name, + workspace_path(tag: tag.name), + class: "badge badge--tag #{'badge--tag-active' if params[:tag] == tag.name}", + draggable: false, + data: { turbo_frame: "_top" } %> + <% end %> + + <% if unread > 0 %> + "><%= unread %> + <% end %> + <%= time_ago_in_words(plan.updated_at) %> ago + <%= link_to user_avatar(plan.created_by_user), profile_path_for(plan.created_by_user), title: plan.created_by_user.name, draggable: false, data: { turbo_frame: "_top" } %> + <%# Bookmark = the keyboard/no-drag path into the folder navigator. + Saved rows unsave on the second click, quietly. %> + " + data-action="coplan--folder-picker#open" + data-move-url="<%= move_to_folder_plan_path(plan) %>" + data-current-folder-id="<%= my_folder_id %>" + data-saved="<%= my_folder_id.present? %>" + data-plan-title="<%= plan.title %>" + title="<%= my_folder_id ? "Saved — click to remove from your library" : "Save to a folder in your library" %>" + aria-label="<%= my_folder_id ? "Remove “#{plan.title}” from your library" : "Save “#{plan.title}” to your library" %>"> + " stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> + + + <% if summary.present? %> + <%= summary %> <% end %> - <% if summary.present? %> - <%= summary %> - <% end %> diff --git a/engine/app/views/coplan/plans/_references.html.erb b/engine/app/views/coplan/plans/_references.html.erb index 9884cea6..770d93d2 100644 --- a/engine/app/views/coplan/plans/_references.html.erb +++ b/engine/app/views/coplan/plans/_references.html.erb @@ -1,21 +1,31 @@ - - - + Add Reference - <%= form_with url: plan_references_path(plan), method: :post, class: "references-add__form" do |f| %> + <%# The visible "+" lives in the footnote-section header (outside this + container); the modal stays inside #plan-references on purpose: the + create action replaces this container via turbo_stream, which closes + the lightbox the moment the reference lands. %> + <% if current_user %> + <%= render layout: "coplan/shared/add_modal", locals: { id: "add-reference-modal", title: "Add a reference" } do %> + <%= form_with url: plan_references_path(plan), method: :post, class: "add-modal__form" do |f| %> - <%= f.text_field :url, placeholder: "https://...", required: true, name: "reference[url]" %> + <%= f.label :url, "URL", for: "reference_url" %> + <%# autofocus is honored by showPopover() — keyboard users land in + the form instead of staying on the trigger button. %> + <%= f.text_field :url, id: "reference_url", placeholder: "https://...", required: true, autofocus: true, name: "reference[url]" %> - <%= f.text_field :title, placeholder: "Title (optional)", name: "reference[title]" %> + <%= f.label :title, "Title", for: "reference_title" %> + <%= f.text_field :title, id: "reference_title", placeholder: "Optional", name: "reference[title]" %> - <%= f.text_field :key, placeholder: "Key (optional, e.g. auth-repo)", name: "reference[key]" %> + <%= f.label :key, "Key", for: "reference_key" %> + <%= f.text_field :key, id: "reference_key", placeholder: "Optional — e.g. auth-repo", name: "reference[key]" %> + + + <%= f.submit "Add reference", class: "btn btn--primary btn--sm" %> - <%= f.submit "Add", class: "btn btn--primary btn--sm" %> <% end %> - - + <% end %> + <% end %> <% if references.any? %> @@ -37,7 +47,7 @@ <% end %> - <% if ref.source == "explicit" %> + <% if ref.source == "explicit" && current_user && CoPlan::PlanPolicy.new(current_user, plan).update? %> <%= button_to "✕", plan_reference_path(plan, ref), method: :delete, class: "references-list__remove", title: "Remove reference", data: { turbo_confirm: "Remove this reference?", turbo_stream: true } %> <% end %> diff --git a/engine/app/views/coplan/plans/_shelves.html.erb b/engine/app/views/coplan/plans/_shelves.html.erb new file mode 100644 index 00000000..ce830e3e --- /dev/null +++ b/engine/app/views/coplan/plans/_shelves.html.erb @@ -0,0 +1,52 @@ +<%# Folder-jump discovery strip: every library shelf this plan sits on, + rendered Drive-style — a quiet folder chip with just the folder name. + Whose library it is lives in the tooltip (and the link target). Lives + outside the broadcast #plan-header region — it's viewer-relative + (needs current_user). %> +<% my_placement = placements.find { |p| p.library.writable_by?(current_user) } %> + +<% if current_user || placements.any? %> + + <% placements.each do |placement| %> + <% mine = placement.library.writable_by?(current_user) %> + <% owner = placement.library.owner %> + <% owner_name = owner.respond_to?(:name) ? owner.name : placement.library.name %> + <% title = mine ? "In your library — #{placement.folder.path}" : "In #{owner_name}’s library — #{placement.folder.path}" %> + <%# Your own shelf opens the folder in your workspace; someone else's + opens their profile at that shelf (the library's public face). %> + <% href = if mine + plans_path(folder: placement.folder_id) + elsif owner.is_a?(CoPlan::User) + profile_path(owner.username.presence || owner.id, anchor: "folder-#{placement.folder_id}") + else + library_path(placement.library, anchor: "folder-#{placement.folder_id}") + end %> + <%= link_to href, class: "plan-shelves__chip#{' plan-shelves__chip--theirs' unless mine}", title: title do %> + + <%= placement.folder.name %> + <% unless mine %> + <%= owner_name.split.first %> + <% end %> + <% end %> + <% end %> + + <% if current_user %> + <%# The bookmark: click to save this plan into your own library via + the folder navigator; click again to quietly unsave it (your own + plans just return to My Plans, unfiled). Filled once shelved — + exactly a bookmark star. %> + " + data-action="coplan--folder-picker#open" + data-move-url="<%= move_to_folder_plan_path(plan) %>" + data-current-folder-id="<%= my_placement&.folder_id %>" + data-saved="<%= my_placement.present? %>" + data-plan-title="<%= plan.title %>" + title="<%= my_placement ? "Saved in #{my_placement.folder.path} — click to remove" : "Save to a folder in your library" %>" + aria-label="<%= my_placement ? "Remove from your library" : "Save to your library" %>"> + " stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> + <% unless my_placement %>Save<% end %> + + <% end %> + +<% end %> diff --git a/engine/app/views/coplan/plans/_sidebar.html.erb b/engine/app/views/coplan/plans/_sidebar.html.erb index c498751b..fdc67dd0 100644 --- a/engine/app/views/coplan/plans/_sidebar.html.erb +++ b/engine/app/views/coplan/plans/_sidebar.html.erb @@ -1,87 +1,128 @@ - diff --git a/engine/app/views/coplan/plans/_version_item.html.erb b/engine/app/views/coplan/plans/_version_item.html.erb index 36b97b93..27356909 100644 --- a/engine/app/views/coplan/plans/_version_item.html.erb +++ b/engine/app/views/coplan/plans/_version_item.html.erb @@ -6,7 +6,7 @@ <% if version.actor_user %> <%= user_avatar(version.actor_user) %> <%= version.actor_user.name %> <% else %> - <%= version.actor_type %> + <%= version.actor_type %> <% end %> <% if version.change_summary.present? %> diff --git a/engine/app/views/coplan/plans/edit.html.erb b/engine/app/views/coplan/plans/edit.html.erb deleted file mode 100644 index 1faac574..00000000 --- a/engine/app/views/coplan/plans/edit.html.erb +++ /dev/null @@ -1,21 +0,0 @@ - - Edit: <%= @plan.title %> - - -<%= form_with model: @plan, url: plan_path(@plan), method: :patch, class: "plan-form card" do |f| %> - - <%= f.label :title %> - <%= f.text_field :title, required: true %> - - - - <%= label_tag "plan_tag_names", "Tags" %> - <%= text_field_tag "plan[tag_names]", @plan.tag_names.join(", "), id: "plan_tag_names", placeholder: "security, api-design" %> - Comma-separated. Reuse existing tags where possible. - - - - <%= f.submit "Save Changes", class: "btn btn--primary" %> - <%= link_to "Cancel", plan_path(@plan), class: "btn btn--secondary" %> - -<% end %> diff --git a/engine/app/views/coplan/plans/edit_content.html.erb b/engine/app/views/coplan/plans/edit_content.html.erb index ee32b2a7..0d1eb21d 100644 --- a/engine/app/views/coplan/plans/edit_content.html.erb +++ b/engine/app/views/coplan/plans/edit_content.html.erb @@ -46,9 +46,36 @@