Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Project aliases #2644

Draft
wants to merge 40 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
c94f5e2
Generate model ProjectAlias
mo-nathan Dec 26, 2024
b8f3756
Rubocop
mo-nathan Dec 26, 2024
3ac26fb
Hookup ProjectAlias class
mo-nathan Dec 26, 2024
2229d1c
Fix migration to use integers for references
mo-nathan Dec 26, 2024
12c738d
Rubocop
mo-nathan Dec 26, 2024
5aac91a
Merge branch 'main' into njw-project-aliases
mo-nathan Dec 29, 2024
fd9e403
Some simple tests
mo-nathan Dec 29, 2024
744d015
Merge branch 'main' into njw-project-aliases
mo-nathan Dec 31, 2024
f5e79bb
Merge branch 'main' into njw-project-aliases
mo-nathan Jan 5, 2025
2ebaf43
Merge branch 'main' into njw-project-aliases
mo-nathan Jan 7, 2025
ec1e816
First pass at CRUD
mo-nathan Jan 10, 2025
829ea96
Rejigger ProjectAliases
mo-nathan Jan 11, 2025
6fe4e88
Merge branch 'main' into njw-project-aliases
mo-nathan Jan 11, 2025
a1ce799
Rubocop
mo-nathan Jan 11, 2025
8482da1
Merge branch 'main' into njw-project-aliases
mo-nathan Jan 12, 2025
33946f2
Project Alias improvements
mo-nathan Jan 13, 2025
bd5baf9
Merge branch 'main' into njw-project-aliases
mo-nathan Jan 14, 2025
24233b7
Merge branch 'main' into njw-project-aliases
mo-nathan Jan 14, 2025
fb4e4b7
Merge branch 'main' into njw-project-aliases
mo-nathan Jan 15, 2025
2ad9e33
Merge branch 'main' into njw-project-aliases
mo-nathan Jan 16, 2025
ab0ccf8
Get autocompleter to switch from location to user
mo-nathan Jan 17, 2025
16236da
Autocomplete fixes
mo-nathan Jan 17, 2025
441c144
Fix new action for ProjectAliases
mo-nathan Jan 18, 2025
9493462
Merge branch 'main' into njw-project-aliases
mo-nathan Jan 18, 2025
35a2c25
Project Alias form fixes
mo-nathan Jan 18, 2025
339eb57
Add more parens
mo-nathan Jan 18, 2025
ea2d105
Fixes for Project Alias delete routes
mo-nathan Jan 18, 2025
12a46ed
Add tests for Project Alias controller
mo-nathan Jan 18, 2025
edecd37
Rubocop
mo-nathan Jan 18, 2025
bdb6a9e
Merge branch 'main' into njw-project-aliases
mo-nathan Jan 19, 2025
d8f0ba4
PR feedback
mo-nathan Jan 19, 2025
e6ed733
Switch to underscore test names
mo-nathan Jan 19, 2025
ebd42a5
Switch to submit button and cleanup button name
mo-nathan Jan 19, 2025
15d7e37
Add links to targets and remove show link
mo-nathan Jan 19, 2025
e63b77e
Add alias lookup when saving a field slip form
mo-nathan Jan 19, 2025
aff4fd3
Fix broken tests
mo-nathan Jan 22, 2025
cd9ad1b
Merge branch 'main' into njw-project-aliases
mo-nathan Jan 22, 2025
120b5c2
Merge branch 'main' into njw-project-aliases
mo-nathan Jan 23, 2025
d9fd3a0
Update user_str based on new interface for lookup_unique_text_name
mo-nathan Jan 23, 2025
8ed460f
Merge branch 'main' into njw-project-aliases
mo-nathan Jan 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions app/controllers/projects/aliases_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# frozen_string_literal: true

module Projects
class AliasesController < ApplicationController
before_action :login_required
before_action :pass_query_params, except: [:index]
before_action :set_project_alias, only: [:show, :edit, :update, :destroy]

def index
@project = Project.find(params[:project_id])
@project_aliases = ProjectAlias.all
respond_to do |format|
format.html
format.json { render(json: @project_aliases) }
end
end

def show
respond_to do |format|
format.html
format.json { render(json: @project_alias) }
end
end

def new
@project_alias = ProjectAlias.new(project_id: params.require(:project_id))
end

def edit; end

def create
@project_alias = ProjectAlias.new(project_alias_params)

respond_to do |format|
if @project_alias.save
format.html do
project_alias_redirect(@project_alias)
end
else
@project_alias = ProjectAlias.new(project_id: params[:project_id])
format.html { render(:new) }
format.json do
render(json: @project_alias.errors, status: :unprocessable_entity)
end
end
end
end

def update
respond_to do |format|
if @project_alias.update(project_alias_params)
format.html do
redirect_to(project_alias_path(
project_id: @project_alias.project_id,
id: @project_alias.id
),
notice: :project_alias_updated.t)
end
format.json { render(json: @project_alias) }
else
format.html { render(:edit) }
format.json do
render(json: @project_alias.errors, status: :unprocessable_entity)
end
end
end
end

def destroy
project_id = @project_alias.project_id
@project_alias.destroy
respond_to do |format|
format.html do
redirect_to(project_aliases_path(project_id:),
notice: :project_alias_destroyed.t)
end
format.json { head(:no_content) }
end
end

private

def project_alias_redirect(project_alias)
redirect_to(project_alias_path(
project_id: project_alias.project_id,
id: project_alias.id
),
notice: :project_alias_created.t)
end

def set_project_alias
@project_alias = ProjectAlias.find(params[:id])
end

def project_alias_params
params.require(:project_alias).permit(:name, :project_id, :target_type,
:location_id, :user_id)
end
end
end
1 change: 1 addition & 0 deletions app/javascript/controllers/autocompleter_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ export default class extends Controller {
// to refresh the primer (as with location_containing a changed lat/lng).
//
swap({ detail }) {
console.log("swap called");
Copy link
Contributor

@nimmolo nimmolo Jan 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this Stimulus controller has a method verbose. If you switch your

console.log("swap called")

to

this.verbose("swap called")

and then uncomment the actual method body of verbose, it will log a whole trace of what it's executing (as defined by wherever Jason and I left lines that say this.verbose("Say something");

However that may give you more info than you want.

let type;
if (this.hasSelectTarget) {
type = this.selectTarget.value;
Expand Down
1 change: 1 addition & 0 deletions app/models/location.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ class Location < AbstractModel # rubocop:disable Metrics/ClassLength
has_many :interests, as: :target, dependent: :destroy, inverse_of: :target
has_many :observations
has_many :projects
has_many :project_aliases, as: :target, dependent: :destroy
has_many :species_lists
has_many :herbaria # should be at most one, but nothing preventing more
has_many :users # via profile location
Expand Down
2 changes: 2 additions & 0 deletions app/models/project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ class Project < AbstractModel # rubocop:disable Metrics/ClassLength
has_many :project_species_lists, dependent: :destroy
has_many :species_lists, through: :project_species_lists

has_many :aliases, class_name: "ProjectAlias", dependent: :destroy

before_destroy :orphan_drafts
validates :field_slip_prefix, uniqueness: true, allow_blank: true
validates :field_slip_prefix,
Expand Down
20 changes: 20 additions & 0 deletions app/models/project_alias.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

class ProjectAlias < ApplicationRecord
belongs_to :target, polymorphic: true
belongs_to :project

validates :name, presence: true

def location_id=(id)
self.target_id = id
end

def user_id=(id)
self.target_id = id
end

def target_type=(type)
self[:target_type] = type.capitalize
end
end
1 change: 1 addition & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,7 @@ class User < AbstractModel # rubocop:disable Metrics/ClassLength
has_many :projects_created, class_name: "Project"
has_many :project_members, dependent: :destroy
has_many :projects, through: :project_members, source: :projects
has_many :project_aliases, as: :target, dependent: :destroy
has_many :publications
has_many :queued_emails
has_many :sequences
Expand Down
51 changes: 51 additions & 0 deletions app/views/controllers/projects/aliases/_form.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<%
@container = :text
type = (project_alias.target_type || :location).downcase.to_sym
value = project_alias.target&.name || ""
%>
<%= form_with(model: project_alias,
data: { controller: :autocompleter, type: }) do |form| %>
<% if project_alias.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(project_alias.errors.count, "error") %> prohibited this project alias from being saved:</h2>
<ul>
<% project_alias.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>

<div class="field">
<%= text_field_with_label(form:, field: :name, label: "#{:Name.t}:", inline: true) %>
</div>
mo-nathan marked this conversation as resolved.
Show resolved Hide resolved

<div class="field">
<%= form.hidden_field(:project_id, value: project_alias.project_id) %>
</div>

<%= tag.div(class: "form-group dropdown",
data: { autocompleter_target: "wrap" } ) do %>
<div class="field">
<%= select_with_label(form:,
field: :target_type,
label: "#{:project_alias_type.t}:",
options: [[:USER.l, :user], [:LOCATION.l, :location]],
inline: true,
selected: type,
data: { autocompleter_target: "select",
action: "autocompleter#swap" }) %>
</div>

<%= autocompleter_hidden_field(form:, type:, hidden_value: project_alias.target_id) %>
<%= form.text_field(
:term, value: value, class: "form-control",
data: { autocompleter_target: "input" }
) %>
<%= autocompleter_dropdown %>
<% end %>

<div class="actions">
<%= form.submit %>
</div>
mo-nathan marked this conversation as resolved.
Show resolved Hide resolved
<% end %>
12 changes: 12 additions & 0 deletions app/views/controllers/projects/aliases/edit.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<%
add_project_banner(@project_alias.project)
@container = :wide
mo-nathan marked this conversation as resolved.
Show resolved Hide resolved
project_id = @project_alias.project_id
%>

<h1>Editing Project Alias</h1>

<%= render('form', project_alias: @project_alias) %>

<%= link_to 'Show', project_alias_path(project_id:, id: @project_alias.id) %> |
<%= link_to 'Back', project_aliases_path(project_id:) %>
36 changes: 36 additions & 0 deletions app/views/controllers/projects/aliases/index.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<%
add_project_banner(@project)
@container = :wide
project_id = @project.id
%>

<h1>Project Aliases</h1>

<table class="table table-striped table-project-members mt-3">
<thead>
<tr>
<th>Name</th>
<th>Target Type</th>
<th>Target</th>
<th>Actions</th>
</tr>
</thead>

<tbody>
<% @project_aliases.each do |project_alias| %>
<tr>
<td><%= project_alias.name %></td>
<td><%= project_alias.target_type %></td>
<td><%= project_alias.target.try(:name) %></td>
<td>
<%= link_to('Show', project_alias_path(project_id:, id: project_alias.id)) %>
<%= link_to('Edit', edit_project_alias_path(project_id:, id: project_alias.id)) %>
<%= link_to('Delete', project_alias_path(project_id:, id: project_alias.id),
data: { turbo_method: :delete, turbo_confirm: 'Are you sure?' }) %>
</td>
</tr>
<% end %>
</tbody>
</table>

<%= link_to 'New Project Alias', new_project_alias_path(project_id:) %>
11 changes: 11 additions & 0 deletions app/views/controllers/projects/aliases/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<%
add_project_banner(@project_alias.project)
@container = :wide
mo-nathan marked this conversation as resolved.
Show resolved Hide resolved
project_id = @project_alias.project_id
%>

<h1>New Project Alias</h1>

<%= render('form', project_alias: @project_alias) %>

<%= link_to('Back', project_aliases_path(project_id: @project_alias.project_id)) %>
23 changes: 23 additions & 0 deletions app/views/controllers/projects/aliases/show.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<%
add_project_banner(@project_alias.project)
@container = :wide
project_id = @project_alias.project_id
%>

<p>
<strong>Name:</strong>
<%= @project_alias.name %>
</p>

<p>
<strong>Target Type:</strong>
<%= @project_alias.target_type %>
</p>

<p>
<strong>Target:</strong>
<%= @project_alias.target.try(:name) %>
</p>

<%= link_to 'Edit', edit_project_alias_path(project_id:, id: @project_alias.id) %> |
<%= link_to 'Back', project_aliases_path %>
6 changes: 6 additions & 0 deletions config/locales/en.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2893,6 +2893,12 @@
field_slips_too_many_field_slips: You have requested more than [MAX] slips.
field_slips_one_per_page: One field slip per page. Print shops prefer this. Otherwise 6/page which works best on US letter page.

# Project Aliases
project_alias_type: Alias Type
project_alias_created: Field slip was successfully created.
project_alias_destroyed: Field slip was successfully destroyed.
project_alias_updated: Field slip was successfully updated.

##############################################################################

# LESS POPULAR PAGES
Expand Down
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -662,6 +662,7 @@ def route_actions_hash
controller: "projects/locations"
resources :members, only: [:new, :create, :edit, :update, :index],
controller: "projects/members", param: :candidate
resources :aliases, controller: "projects/aliases"
resources :violations, only: [:index], controller: "projects/violations"
end
# resourceful route won't work because it requires an additional id
Expand Down
18 changes: 18 additions & 0 deletions db/migrate/20241226192904_create_project_aliases.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

class CreateProjectAliases < ActiveRecord::Migration[7.1]
def change
create_table(:project_aliases) do |t|
t.integer(:project_id, null: false)
t.integer(:target_id, null: false)
t.string(:target_type, null: false)
t.string(:name)

t.timestamps

t.foreign_key(:projects)
t.index([:target_type, :target_id])
t.index(:project_id)
end
end
end
14 changes: 13 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[7.2].define(version: 2024_12_09_041049) do
ActiveRecord::Schema[7.2].define(version: 2024_12_26_192904) do
create_table "api_keys", id: :integer, charset: "utf8mb3", force: :cascade do |t|
t.datetime "created_at", precision: nil
t.datetime "last_used", precision: nil
Expand Down Expand Up @@ -558,6 +558,17 @@
t.datetime "updated_at", null: false
end

create_table "project_aliases", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
t.integer "project_id", null: false
t.integer "target_id", null: false
t.string "target_type", null: false
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["project_id"], name: "index_project_aliases_on_project_id"
t.index ["target_type", "target_id"], name: "index_project_aliases_on_target_type_and_target_id"
end

create_table "project_images", charset: "utf8mb3", force: :cascade do |t|
t.integer "image_id", null: false
t.integer "project_id", null: false
Expand Down Expand Up @@ -965,6 +976,7 @@
t.index ["observation_id"], name: "observation_index"
end

add_foreign_key "project_aliases", "projects"
add_foreign_key "solid_queue_blocked_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
add_foreign_key "solid_queue_claimed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
add_foreign_key "solid_queue_failed_executions", "solid_queue_jobs", column: "job_id", on_delete: :cascade
Expand Down
Loading
Loading