From 1ec0b3e888ebc8d343716561e7e360fc195de46f Mon Sep 17 00:00:00 2001 From: James Johnson Date: Mon, 10 Jun 2024 08:58:45 -0500 Subject: [PATCH] Add notes to urls (#185) --- app/controllers/urls_controller.rb | 16 ++++- app/datatables/url_datatable.rb | 1 + app/models/url.rb | 1 + app/policies/url_policy.rb | 4 ++ app/views/urls/_short_url_table_cell.html.erb | 4 +- app/views/urls/_sleek_form.html.erb | 19 ++++-- app/views/urls/show.html.erb | 17 +++++ config/routes.rb | 12 ++-- cypress/e2e/urlNotes.cy.ts | 64 +++++++++++++++++++ db/migrate/20240605133839_add_url_note.rb | 5 ++ db/schema.rb | 3 +- 11 files changed, 134 insertions(+), 12 deletions(-) create mode 100644 cypress/e2e/urlNotes.cy.ts create mode 100644 db/migrate/20240605133839_add_url_note.rb diff --git a/app/controllers/urls_controller.rb b/app/controllers/urls_controller.rb index a8cad791..60d66f61 100644 --- a/app/controllers/urls_controller.rb +++ b/app/controllers/urls_controller.rb @@ -2,7 +2,7 @@ class UrlsController < ApplicationController before_action :ensure_signed_in before_action :set_url, only: %i[edit update destroy] - before_action :set_url_friendly, only: [:show] + before_action :set_url_friendly, only: %i[show update_note] before_action :set_group, only: %i[index create] # GET /urls @@ -111,6 +111,18 @@ def update end end + # PUT/PATCH /urls/1/note + def update_note + respond_to do |format| + if @url.update(url_params) + format.html { redirect_to url_path(@url.keyword), notice: 'Note saved.' } + else + flash[:error] = @url.errors.full_messages.join(', ') + format.html { redirect_to url_path(@url.keyword) } + end + end + end + # DELETE /urls/1 # DELETE /urls/1.json def destroy @@ -161,7 +173,7 @@ def set_url_friendly # Never trust parameters from the scary internet, # only permit the allowlist through. def url_params - params.require(:url).permit(:url, :keyword, :group_id, :modified_by) + params.require(:url).permit(:url, :keyword, :group_id, :modified_by, :note) end def set_group diff --git a/app/datatables/url_datatable.rb b/app/datatables/url_datatable.rb index 05a6270f..9da70c04 100644 --- a/app/datatables/url_datatable.rb +++ b/app/datatables/url_datatable.rb @@ -13,6 +13,7 @@ def view_columns group_name: { source: 'Group.name' }, url: { source: 'Url.url' }, keyword: { source: 'Url.keyword' }, + note: { source: 'Url.note' }, total_clicks: { source: 'Url.total_clicks', searchable: false }, created_at: { source: 'Url.created_at' }, # needed for actions like edit, delete, etc. diff --git a/app/models/url.rb b/app/models/url.rb index 3ca880d1..96a425ab 100644 --- a/app/models/url.rb +++ b/app/models/url.rb @@ -40,6 +40,7 @@ class Url < ApplicationRecord multiline: true, message: 'special characters are not permitted. Only letters, and numbers, dashes ("-") and underscores ("_")' } + validates :note, length: { maximum: 1000 }, allow_blank: true validate :check_for_valid_url before_validation do diff --git a/app/policies/url_policy.rb b/app/policies/url_policy.rb index 24fd6ecc..d9c54d5f 100644 --- a/app/policies/url_policy.rb +++ b/app/policies/url_policy.rb @@ -23,6 +23,10 @@ def update? user_has_access? end + def update_note? + user_has_access? + end + def destroy? user_has_access? end diff --git a/app/views/urls/_short_url_table_cell.html.erb b/app/views/urls/_short_url_table_cell.html.erb index cb33f3f4..1f487ee8 100644 --- a/app/views/urls/_short_url_table_cell.html.erb +++ b/app/views/urls/_short_url_table_cell.html.erb @@ -1,4 +1,3 @@ -
<%= link_to(display_keyword_url(url.keyword), full_url(url), target: '_blank') %>
<%= f.hidden_field :group_id, value: @group.id if @group %> - + <% if !is_edit %> + + <% end %> <% if is_edit %> - <%= t('views.urls.sleek_form.edit.cancel')%> +
+ <%= f.label :note, "Note", class: 'tw-text-xs tw-font-bold tw-absolute', style: "top: 0.5rem; left: 1rem;" %> + <%= f.text_area :note, class: "tw-w-full tw-border-none tw-text-sm tw-rounded-lg !tw-text-black", style: "padding-top: 1.5rem; padding-left: 1rem; border-radius: 0.25rem;", data: { cy: 'note-input' } %> +
+
+ + +
<% end %>
diff --git a/app/views/urls/show.html.erb b/app/views/urls/show.html.erb index 72a9c5db..5ebd93df 100644 --- a/app/views/urls/show.html.erb +++ b/app/views/urls/show.html.erb @@ -86,6 +86,23 @@
+
+
+
+
Note
+
+ <%= form_with model: @url, url: note_url_path(@url.keyword), method: :patch, data: { cy: 'note-form' } do |form|%> + <%= form.label :note, "Note", class: 'sr-only' %> + <%= form.text_area :note, class: 'form-control', placeholder: 'Add a note', data: { cy: 'note-input' } %> +
+ <%= button_tag 'Reset', type: 'reset', class: "tw-uppercase tw-text-xs tw-bg-neutral-100 tw-px-4 tw-py-2 tw-rounded hover:tw-bg-neutral-200 tw-transition" %> + <%= form.button "Save", class: "btn btn-primary" %> +
+ <% end %> +
+
+
+
diff --git a/config/routes.rb b/config/routes.rb index b37ea179..d598c265 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -63,6 +63,10 @@ on: :collection, to: "url_csvs#show_aggregated", as: "csv" + member do + patch :note, to: 'urls#update_note' + end + patch :note, on: :member, to: 'urls#update_note' end # transfer_requests transfer_requests index get @@ -130,9 +134,9 @@ # admin/urls/create admin::urls create put resources :urls, only: %i[index edit show update destroy create] do get "csv/:duration/:time_unit", - on: :collection, - to: "url_csvs#show", - as: "csv" + on: :collection, + to: "url_csvs#show", + as: "csv" end # admin/audits/:search admin::urls index get @@ -143,7 +147,7 @@ # admin/groups/:id # admin/groups/:id/urls # admin/groups/:id/members - resources :groups, only: [:index, :show] do + resources :groups, only: %i[index show] do resources :urls, only: [:index], controller: "group_urls" resources :members, only: [:index], controller: "group_members" end diff --git a/cypress/e2e/urlNotes.cy.ts b/cypress/e2e/urlNotes.cy.ts new file mode 100644 index 00000000..7ca63cee --- /dev/null +++ b/cypress/e2e/urlNotes.cy.ts @@ -0,0 +1,64 @@ +describe("create zlink in collection", () => { + let url; + beforeEach(() => { + cy.app("clean"); + cy.createUser("testuser").then((user) => { + url = cy.createUrl({ + keyword: "cla", + url: "https://cla.umn.edu", + group_id: user.context_group_id, + }); + }); + + cy.login("testuser"); + }); + + context("url show page (aka stats page)", () => { + beforeEach(() => { + cy.visit("/shortener/urls/cla"); + }); + + it("adds a note to a url", () => { + cy.get("[data-cy='note-form']").within(() => { + cy.get("[data-cy='note-input']").type("This is a note"); + cy.contains("Save").click(); + }); + + cy.contains("Note saved").should("be.visible"); + + // reload the page + cy.visit("/shortener/urls/cla"); + // check that the note is there + cy.get("[data-cy='note-input']").should("contain", "This is a note"); + }); + + it("limits the note contents to 1000 characters", () => { + cy.get("[data-cy='note-form']").within(() => { + cy.get("[data-cy='note-input']").type("a".repeat(1001), { delay: 0 }); + cy.contains("Save").click(); + }); + + cy.contains("Note is too long").should("be.visible"); + }); + }); + + context("url index page", () => { + beforeEach(() => { + cy.visit("/shortener/urls"); + }); + + it("edits a note and shows it in the table", () => { + cy.get("#urls-table").contains("cla").closest("tr").as("claRow"); + cy.get("@claRow").find(".dropdown").as("claDropdown"); + cy.get("@claDropdown").find(".actions-dropdown-button").click(); + cy.get("@claDropdown").contains("Edit").click(); + + cy.get("[data-cy='note-input']").type("edited note"); + + cy.contains("Submit").click(); + + // check that the note is in the table row + cy.get("@claRow").contains("edited note").should("be.visible"); + }); + }); +}); diff --git a/db/migrate/20240605133839_add_url_note.rb b/db/migrate/20240605133839_add_url_note.rb new file mode 100644 index 00000000..c60a4905 --- /dev/null +++ b/db/migrate/20240605133839_add_url_note.rb @@ -0,0 +1,5 @@ +class AddUrlNote < ActiveRecord::Migration[7.1] + def change + add_column :urls, :note, :text, null: true, default: nil + end +end diff --git a/db/schema.rb b/db/schema.rb index 771a648f..76c20f36 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.0].define(version: 2023_01_10_212461) do +ActiveRecord::Schema[7.1].define(version: 2024_06_05_133839) do create_table "clicks", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.string "country_code" t.integer "url_id" @@ -104,6 +104,7 @@ t.integer "modified_by" t.datetime "created_at", precision: nil, null: false t.datetime "updated_at", precision: nil, null: false + t.text "note" t.index ["group_id"], name: "index_urls_on_group_id" t.index ["keyword"], name: "index_urls_on_keyword", unique: true end