From 9642f9966f60cbf9cc9e56ddd698cf400fef9641 Mon Sep 17 00:00:00 2001 From: Paul Mesnilgrente Date: Tue, 10 Oct 2023 16:14:57 +0200 Subject: [PATCH 01/18] feat(pipeline): default order is last_updated_at first --- app/controllers/pipelines_controller.rb | 4 ++-- app/frontend/entrypoints/application.js | 2 +- app/frontend/js/ContentSourceFilter.js | 9 --------- app/frontend/js/PipelineSort.js | 9 +++++++++ app/views/pipelines/index.html.erb | 7 ++++--- 5 files changed, 16 insertions(+), 15 deletions(-) delete mode 100644 app/frontend/js/ContentSourceFilter.js create mode 100644 app/frontend/js/PipelineSort.js diff --git a/app/controllers/pipelines_controller.rb b/app/controllers/pipelines_controller.rb index 6a8eb518..0e2b2d67 100644 --- a/app/controllers/pipelines_controller.rb +++ b/app/controllers/pipelines_controller.rb @@ -79,8 +79,8 @@ def find_pipeline end def assign_sort_by - @sort_by = { name: :asc } - @sort_by = { updated_at: :desc } if params['sort_by'] == 'updated_at' + @sort_by = { updated_at: :desc } + @sort_by = { name: :asc } if params['sort_by'] == 'name' end def pipeline_params diff --git a/app/frontend/entrypoints/application.js b/app/frontend/entrypoints/application.js index b306a800..ab8e13bf 100644 --- a/app/frontend/entrypoints/application.js +++ b/app/frontend/entrypoints/application.js @@ -37,7 +37,7 @@ import "/js/TestTransformationRecordSelector"; import "/js/TestDestination"; import "/js/Tooltips"; import "/js/CollapseScroll"; -import "/js/ContentSourceFilter"; +import "/js/PipelineSort"; import "/js/CreateModal"; import "/js/AutoComplete"; diff --git a/app/frontend/js/ContentSourceFilter.js b/app/frontend/js/ContentSourceFilter.js deleted file mode 100644 index 83addaa4..00000000 --- a/app/frontend/js/ContentSourceFilter.js +++ /dev/null @@ -1,9 +0,0 @@ -const contentSourceFilter = document.getElementById("js-content-source-filter"); - -if (contentSourceFilter) { - const form = contentSourceFilter.closest("form"); - - contentSourceFilter.addEventListener("change", (event) => { - form.submit(); - }); -} diff --git a/app/frontend/js/PipelineSort.js b/app/frontend/js/PipelineSort.js new file mode 100644 index 00000000..ebe310cb --- /dev/null +++ b/app/frontend/js/PipelineSort.js @@ -0,0 +1,9 @@ +const selectElement = document.getElementById("js-pipeline-sort"); + +if (selectElement) { + const form = selectElement.closest("form"); + + selectElement.addEventListener("change", () => { + form.submit(); + }); +} diff --git a/app/views/pipelines/index.html.erb b/app/views/pipelines/index.html.erb index 54808a7a..94db3ad0 100644 --- a/app/views/pipelines/index.html.erb +++ b/app/views/pipelines/index.html.erb @@ -20,10 +20,11 @@
  • <%= form_with url: '/pipelines', method: :get do |form| %> - <%= select_tag( + <%= form.select( :sort_by, - options_for_select([['Alphabetical', 'name'], ['Last Edited', 'updated_at']], @sort_by.first), - class: 'form-select', id: 'js-content-source-filter' + [['Last Edited', 'updated_at'], ['Alphabetical', 'name']], + { selected: @sort_by.keys.first }, + class: 'form-select', id: 'js-pipeline-sort' ) %> <% end %>
  • From 455306227944dd537cafcd5c0c089e876c26c0f9 Mon Sep 17 00:00:00 2001 From: Paul Mesnilgrente Date: Tue, 10 Oct 2023 16:24:06 +0200 Subject: [PATCH 02/18] feat(pipeline): 20 pipelines per page --- app/models/pipeline.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/models/pipeline.rb b/app/models/pipeline.rb index 21cebe2d..7ac8bf93 100644 --- a/app/models/pipeline.rb +++ b/app/models/pipeline.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class Pipeline < ApplicationRecord + paginates_per 20 + has_many :harvest_definitions, dependent: :destroy has_many :harvest_jobs, through: :harvest_definitions belongs_to :last_edited_by, class_name: 'User', optional: true From 4a4adff24ba29b240a421a0735c200b5220e47e4 Mon Sep 17 00:00:00 2001 From: Paul Mesnilgrente Date: Tue, 10 Oct 2023 17:05:11 +0200 Subject: [PATCH 03/18] feat(pipeline): made cards in pipeline clickable --- app/frontend/entrypoints/application.scss | 4 +--- .../js/components/SharedDefinitionsView.jsx | 2 +- app/frontend/stylesheets/_base.scss | 5 ----- app/frontend/stylesheets/blocks/_card-link.scss | 17 +++++++++++++++++ app/frontend/stylesheets/bootstrap/_card.scss | 6 ------ app/views/destinations/index.html.erb | 4 ++-- app/views/jobs/_jobs.html.erb | 2 +- app/views/pipelines/_card.html.erb | 8 ++++++-- app/views/pipelines/index.html.erb | 2 +- app/views/schedules/index.html.erb | 4 ++-- app/views/users/index.html.erb | 2 +- 11 files changed, 32 insertions(+), 24 deletions(-) delete mode 100644 app/frontend/stylesheets/_base.scss create mode 100644 app/frontend/stylesheets/blocks/_card-link.scss diff --git a/app/frontend/entrypoints/application.scss b/app/frontend/entrypoints/application.scss index 891622f3..21e1e932 100644 --- a/app/frontend/entrypoints/application.scss +++ b/app/frontend/entrypoints/application.scss @@ -30,7 +30,7 @@ // Blocks - +@import "../stylesheets/blocks/card-link"; @import "../stylesheets/blocks/field-nav"; @import "../stylesheets/blocks/field-nav-panel"; @import "../stylesheets/blocks/form-in-dropdown"; @@ -39,5 +39,3 @@ @import "../stylesheets/blocks/record-view"; @import "../stylesheets/blocks/definition-group"; @import "../stylesheets/blocks/table"; - -@import "../stylesheets/base"; diff --git a/app/frontend/js/components/SharedDefinitionsView.jsx b/app/frontend/js/components/SharedDefinitionsView.jsx index ec48a09f..e3cc730c 100644 --- a/app/frontend/js/components/SharedDefinitionsView.jsx +++ b/app/frontend/js/components/SharedDefinitionsView.jsx @@ -16,7 +16,7 @@ const SharedDefinitionsView = ({ definitionType }) => { return (
    diff --git a/app/frontend/stylesheets/_base.scss b/app/frontend/stylesheets/_base.scss deleted file mode 100644 index 38a171c4..00000000 --- a/app/frontend/stylesheets/_base.scss +++ /dev/null @@ -1,5 +0,0 @@ -body { - background-color: $bleached-silk; - color: $antarctic-deep; - font-family: 'Lato', sans-serif; -} diff --git a/app/frontend/stylesheets/blocks/_card-link.scss b/app/frontend/stylesheets/blocks/_card-link.scss new file mode 100644 index 00000000..5673553f --- /dev/null +++ b/app/frontend/stylesheets/blocks/_card-link.scss @@ -0,0 +1,17 @@ +// this is used to make a card which has interactive elements +// clickable without breaking a11y +// See https://inclusive-components.design/cards/ +.card--clickable { + &:hover { + border-color: $primary; + } + + .card__link::after { + content: ""; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + } +} diff --git a/app/frontend/stylesheets/bootstrap/_card.scss b/app/frontend/stylesheets/bootstrap/_card.scss index 99ee27a2..be3ea155 100644 --- a/app/frontend/stylesheets/bootstrap/_card.scss +++ b/app/frontend/stylesheets/bootstrap/_card.scss @@ -62,12 +62,6 @@ } } -a.card { - &:hover { - border-color: $primary; - } -} - form { .card { background: $doctor; diff --git a/app/views/destinations/index.html.erb b/app/views/destinations/index.html.erb index aa7ec1b4..28223ec2 100644 --- a/app/views/destinations/index.html.erb +++ b/app/views/destinations/index.html.erb @@ -18,9 +18,9 @@
    <%- @destinations.each do |destination| %>
    - <%= link_to destination, class: 'card mb-3' do %> + <%= link_to destination, class: 'card card--clickable mb-3' do %>
    -
    <%= destination.name %>
    +
    <%= destination.name %>
    <% end %>
    diff --git a/app/views/jobs/_jobs.html.erb b/app/views/jobs/_jobs.html.erb index f6e642c4..f590d053 100644 --- a/app/views/jobs/_jobs.html.erb +++ b/app/views/jobs/_jobs.html.erb @@ -5,7 +5,7 @@ <%= link_to pipeline_harvest_definition_extraction_definition_extraction_job_path( pipeline, harvest_definition, extraction_definition, job ), - class: 'card mb-3' do %> + class: 'card card--clickable mb-3' do %>
    <%= job.name %>
    <%= job.updated_at.to_fs(:light) %>
    diff --git a/app/views/pipelines/_card.html.erb b/app/views/pipelines/_card.html.erb index 68a8508e..3e52fd56 100644 --- a/app/views/pipelines/_card.html.erb +++ b/app/views/pipelines/_card.html.erb @@ -5,9 +5,13 @@ delete_path ||= '' position = type == 'extraction' ? 'first' : 'last' %> -
    +
    -
    <%= definition.name %>
    +
    + <%= link_to edit_path, class: 'card__link' do %> + <%= definition.name %> + <% end %> +
    <%= send("#{type}_card_subtitle", definition) %>
    diff --git a/app/views/pipelines/index.html.erb b/app/views/pipelines/index.html.erb index 94db3ad0..9e333db5 100644 --- a/app/views/pipelines/index.html.erb +++ b/app/views/pipelines/index.html.erb @@ -48,7 +48,7 @@ <%- @pipelines.each do |pipeline| %>
    - <%= link_to pipeline, class: 'card mb-3 d-flex' do %> + <%= link_to pipeline, class: 'card card--clickable mb-3 d-flex' do %>
    <%= pipeline.name %>
    diff --git a/app/views/schedules/index.html.erb b/app/views/schedules/index.html.erb index 9205dbb6..0b7c3279 100644 --- a/app/views/schedules/index.html.erb +++ b/app/views/schedules/index.html.erb @@ -31,7 +31,7 @@
    - <%= link_to new_pipeline_schedule_path(@pipeline), class: 'card card--create-cta mb-3 d-flex' do %> + <%= link_to new_pipeline_schedule_path(@pipeline), class: 'card card--clickable card--create-cta mb-3 d-flex' do %>
    + Create new schedule
    @@ -40,7 +40,7 @@ <%- @schedules.each do |schedule| %>
    - <%= link_to pipeline_schedule_path(@pipeline, schedule), class: 'card mb-3 d-flex' do %> + <%= link_to pipeline_schedule_path(@pipeline, schedule), class: 'card card--clickable mb-3 d-flex' do %>
    <%= schedule.name %>
    diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb index 9764a51b..2e2e3387 100644 --- a/app/views/users/index.html.erb +++ b/app/views/users/index.html.erb @@ -22,7 +22,7 @@
    <% @users.each do |user| %>
    - <%= link_to user, class: 'card mb-3' do %> + <%= link_to user, class: 'card card--clickable mb-3' do %>
    <%= user.username %>

    <%= user.email %>

    From 01174db377104d56ca0955d83d3c94d881e74f77 Mon Sep 17 00:00:00 2001 From: Paul Mesnilgrente Date: Tue, 10 Oct 2023 20:58:41 +0200 Subject: [PATCH 04/18] fix(pipeline): 19 pipelines per page to have the "Create pipeline" button --- app/models/pipeline.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/pipeline.rb b/app/models/pipeline.rb index 7ac8bf93..24f52774 100644 --- a/app/models/pipeline.rb +++ b/app/models/pipeline.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Pipeline < ApplicationRecord - paginates_per 20 + paginates_per 19 # not 20 because of the "Create new pipeline" button has_many :harvest_definitions, dependent: :destroy has_many :harvest_jobs, through: :harvest_definitions From 9e67112b4b1f15d8bb4097d8044152f397651ada Mon Sep 17 00:00:00 2001 From: Paul Mesnilgrente Date: Tue, 10 Oct 2023 20:59:04 +0200 Subject: [PATCH 05/18] refactor(pipeline): used a label and form.row for layout --- app/frontend/stylesheets/bootstrap/_form.scss | 2 +- app/views/pipelines/index.html.erb | 102 ++++++++---------- 2 files changed, 45 insertions(+), 59 deletions(-) diff --git a/app/frontend/stylesheets/bootstrap/_form.scss b/app/frontend/stylesheets/bootstrap/_form.scss index af6e9b2e..2a2b09f0 100644 --- a/app/frontend/stylesheets/bootstrap/_form.scss +++ b/app/frontend/stylesheets/bootstrap/_form.scss @@ -1,3 +1,3 @@ -.form-label { +.form-label, .col-form-label { font-weight: 600; } diff --git a/app/views/pipelines/index.html.erb b/app/views/pipelines/index.html.erb index 9e333db5..22249d6e 100644 --- a/app/views/pipelines/index.html.erb +++ b/app/views/pipelines/index.html.erb @@ -6,74 +6,60 @@

    Pipelines

    - <% end %> -
    -
      - -
    • - - Sort by: - -
    • - -
    • - <%= form_with url: '/pipelines', method: :get do |form| %> - <%= form.select( - :sort_by, - [['Last Edited', 'updated_at'], ['Alphabetical', 'name']], - { selected: @sort_by.keys.first }, - class: 'form-select', id: 'js-pipeline-sort' - ) %> - <% end %> -
    • - -
    +<%= form_with url: '/pipelines', method: :get, class: 'row mb-4' do |form| %> +
    + <%= form.label :sort_by, 'Sort by:', class: 'col-form-label' %>
    +
    + <%= form.select( + :sort_by, + [['Last Edited', 'updated_at'], ['Alphabetical', 'name']], + { selected: @sort_by.keys.first }, + class: 'form-select', id: 'js-pipeline-sort' + ) %> +
    +<% end %> -
    - -
    +
    -
    +
    + +
    + <%- @pipelines.each do |pipeline| %>
    - -
    - - <%- @pipelines.each do |pipeline| %> -
    - <%= link_to pipeline, class: 'card card--clickable mb-3 d-flex' do %> -
    -
    <%= pipeline.name %>
    -
    - <%= last_edited_by(pipeline) %> -
    -
    - - <%= pipeline.harvest_definitions.harvest.count %> Harvest + <%= link_to pipeline, class: 'card card--clickable mb-3 d-flex' do %> +
    +
    <%= pipeline.name %>
    +
    + <%= last_edited_by(pipeline) %> +
    +
    + + <%= pipeline.harvest_definitions.harvest.count %> Harvest + + + <%= pipeline.harvest_definitions.enrichment.count %> Enrichments + + + <% if pipeline.schedules.any? %> + + Scheduled - - <%= pipeline.harvest_definitions.enrichment.count %> Enrichments - - - <% if pipeline.schedules.any? %> - - Scheduled - - <% end %> -
    + <% end %>
    - <% end %> -
    - <%- end %> +
    + <% end %> +
    + <%- end %> -
    +
    <%= render 'shared/pagination_below_table', items: @pipelines %> From 53f8712183b0ca1f8adfc8d3c1a275c57fb0115c Mon Sep 17 00:00:00 2001 From: Paul Mesnilgrente Date: Tue, 10 Oct 2023 21:32:49 +0200 Subject: [PATCH 06/18] feat(pipeline): added filter by name and description --- app/controllers/pipelines_controller.rb | 14 ++++++++------ app/models/pipeline.rb | 7 +++++++ app/views/pipelines/index.html.erb | 10 ++++++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/app/controllers/pipelines_controller.rb b/app/controllers/pipelines_controller.rb index 0e2b2d67..9e5ec101 100644 --- a/app/controllers/pipelines_controller.rb +++ b/app/controllers/pipelines_controller.rb @@ -3,11 +3,10 @@ class PipelinesController < ApplicationController include LastEditedBy - before_action :assign_sort_by, only: %w[index create] before_action :find_pipeline, only: %w[show destroy edit update clone] def index - @pipelines = Pipeline.order(@sort_by).page(params[:page]) + @pipelines = pipelines @pipeline = Pipeline.new end @@ -33,7 +32,7 @@ def create redirect_to pipeline_path(@pipeline), notice: t('.success') else flash.alert = t('.failure') - @pipelines = Pipeline.order(@sort_by).page(params[:page]) + @pipelines = pipelines render :index end end @@ -78,9 +77,12 @@ def find_pipeline @pipeline = Pipeline.find(params[:id]) end - def assign_sort_by - @sort_by = { updated_at: :desc } - @sort_by = { name: :asc } if params['sort_by'] == 'name' + def pipelines + Pipeline.search(params[:search]).order(sort_by).page(params[:page]) + end + + def sort_by + @sort_by ||= params['sort_by'] == 'name' ? { name: :asc } : { updated_at: :desc } end def pipeline_params diff --git a/app/models/pipeline.rb b/app/models/pipeline.rb index 24f52774..811ce91f 100644 --- a/app/models/pipeline.rb +++ b/app/models/pipeline.rb @@ -12,6 +12,13 @@ class Pipeline < ApplicationRecord validates :name, presence: true, uniqueness: true + def self.search(words) + words = sanitize_sql_like(words) + return self if words.empty? + + where('name LIKE ?', "%#{words}%").or(where('description LIKE ?', "%#{words}%")) + end + def harvest harvest_definitions.find_by(kind: 'harvest') end diff --git a/app/views/pipelines/index.html.erb b/app/views/pipelines/index.html.erb index 22249d6e..d8438830 100644 --- a/app/views/pipelines/index.html.erb +++ b/app/views/pipelines/index.html.erb @@ -20,6 +20,16 @@ class: 'form-select', id: 'js-pipeline-sort' ) %>
    +
    +
    + <%= form.label :search, 'Search:', class: 'col-form-label' %> +
    +
    +
    + <%= form.text_field(:search, value: params[:search], class: 'form-control') %> + +
    +
    <% end %>
    From 6eba7c86aec107c08dc7839f0658c799a5fb806b Mon Sep 17 00:00:00 2001 From: Paul Mesnilgrente Date: Tue, 10 Oct 2023 23:08:51 +0200 Subject: [PATCH 07/18] fix(a11y): several a11y fixes --- app/frontend/entrypoints/application.js | 2 +- app/frontend/js/PipelineSort.js | 9 --------- app/frontend/js/SubmittingSelect.js | 11 +++++++++++ app/models/pipeline.rb | 2 +- app/views/layouts/application.html.erb | 2 +- app/views/pipelines/_card.html.erb | 8 ++++---- app/views/pipelines/index.html.erb | 10 +++++----- extractions/staging/.keep | 0 8 files changed, 23 insertions(+), 21 deletions(-) delete mode 100644 app/frontend/js/PipelineSort.js create mode 100644 app/frontend/js/SubmittingSelect.js delete mode 100644 extractions/staging/.keep diff --git a/app/frontend/entrypoints/application.js b/app/frontend/entrypoints/application.js index ab8e13bf..41c259fc 100644 --- a/app/frontend/entrypoints/application.js +++ b/app/frontend/entrypoints/application.js @@ -37,7 +37,7 @@ import "/js/TestTransformationRecordSelector"; import "/js/TestDestination"; import "/js/Tooltips"; import "/js/CollapseScroll"; -import "/js/PipelineSort"; +import "/js/SubmittingSelect"; import "/js/CreateModal"; import "/js/AutoComplete"; diff --git a/app/frontend/js/PipelineSort.js b/app/frontend/js/PipelineSort.js deleted file mode 100644 index ebe310cb..00000000 --- a/app/frontend/js/PipelineSort.js +++ /dev/null @@ -1,9 +0,0 @@ -const selectElement = document.getElementById("js-pipeline-sort"); - -if (selectElement) { - const form = selectElement.closest("form"); - - selectElement.addEventListener("change", () => { - form.submit(); - }); -} diff --git a/app/frontend/js/SubmittingSelect.js b/app/frontend/js/SubmittingSelect.js new file mode 100644 index 00000000..954c28c0 --- /dev/null +++ b/app/frontend/js/SubmittingSelect.js @@ -0,0 +1,11 @@ +const selectElements = document.querySelectorAll( + '[data-submitting-select="true"]' +); + +selectElements.forEach(function (selectElement) { + const form = selectElement.closest("form"); + + selectElement.addEventListener("change", () => { + form.submit(); + }); +}); diff --git a/app/models/pipeline.rb b/app/models/pipeline.rb index 811ce91f..d244e157 100644 --- a/app/models/pipeline.rb +++ b/app/models/pipeline.rb @@ -13,7 +13,7 @@ class Pipeline < ApplicationRecord validates :name, presence: true, uniqueness: true def self.search(words) - words = sanitize_sql_like(words) + words = sanitize_sql_like(words || '') return self if words.empty? where('name LIKE ?', "%#{words}%").or(where('description LIKE ?', "%#{words}%")) diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 85282521..eee56d85 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,5 +1,5 @@ - + <%= content_for?(:title) ? "#{yield(:title)} | " : '' %>Harvester diff --git a/app/views/pipelines/_card.html.erb b/app/views/pipelines/_card.html.erb index 3e52fd56..4bff475a 100644 --- a/app/views/pipelines/_card.html.erb +++ b/app/views/pipelines/_card.html.erb @@ -7,14 +7,14 @@
    -
    +

    <%= link_to edit_path, class: 'card__link' do %> <%= definition.name %> <% end %> -

    -
    +
    +

    <%= send("#{type}_card_subtitle", definition) %> -

    +
    <% if definition.shared? %> Shared (<%= "#{definition.harvest_definitions.count} pipelines" %>) diff --git a/app/views/pipelines/index.html.erb b/app/views/pipelines/index.html.erb index d8438830..509d4d16 100644 --- a/app/views/pipelines/index.html.erb +++ b/app/views/pipelines/index.html.erb @@ -17,7 +17,7 @@ :sort_by, [['Last Edited', 'updated_at'], ['Alphabetical', 'name']], { selected: @sort_by.keys.first }, - class: 'form-select', id: 'js-pipeline-sort' + class: 'form-select', 'data-submitting-select': true ) %>
    @@ -37,7 +37,7 @@
    @@ -46,10 +46,10 @@
    <%= link_to pipeline, class: 'card card--clickable mb-3 d-flex' do %>
    -
    <%= pipeline.name %>
    -
    +

    <%= pipeline.name %>

    +

    <%= last_edited_by(pipeline) %> -

    +
    <%= pipeline.harvest_definitions.harvest.count %> Harvest diff --git a/extractions/staging/.keep b/extractions/staging/.keep deleted file mode 100644 index e69de29b..00000000 From 9c6df619eaf05b53246f6fd907950a2529c90e8b Mon Sep 17 00:00:00 2001 From: Paul Mesnilgrente Date: Wed, 11 Oct 2023 00:39:49 +0200 Subject: [PATCH 08/18] wip(pipeline): adding format as filter --- app/controllers/pipelines_controller.rb | 6 ++++- app/models/extraction_definition.rb | 4 +++- app/models/pipeline.rb | 2 +- app/views/pipelines/index.html.erb | 31 +++++++++++++++++-------- 4 files changed, 30 insertions(+), 13 deletions(-) diff --git a/app/controllers/pipelines_controller.rb b/app/controllers/pipelines_controller.rb index 9e5ec101..3e502f77 100644 --- a/app/controllers/pipelines_controller.rb +++ b/app/controllers/pipelines_controller.rb @@ -78,7 +78,11 @@ def find_pipeline end def pipelines - Pipeline.search(params[:search]).order(sort_by).page(params[:page]) + Pipeline.search(params[:search], format).order(sort_by).page(params[:page]) + end + + def format + @format ||= params['format'] end def sort_by diff --git a/app/models/extraction_definition.rb b/app/models/extraction_definition.rb index 5163e191..b12ebc75 100644 --- a/app/models/extraction_definition.rb +++ b/app/models/extraction_definition.rb @@ -3,6 +3,8 @@ # Used to store the information for running an extraction # class ExtractionDefinition < ApplicationRecord + FORMATS = %w[JSON XML HTML].freeze + # The destination is used for Enrichment Extractions # To know where to pull the records that are to be enriched from belongs_to :destination, optional: true @@ -39,7 +41,7 @@ class ExtractionDefinition < ApplicationRecord # Harvest related validation with_options if: :harvest? do - validates :format, presence: true, inclusion: { in: %w[JSON XML HTML] } + validates :format, presence: true, inclusion: { in: FORMATS } validates :base_url, presence: true, format: { with: URI::DEFAULT_PARSER.make_regexp } validate :total_selector_format validates :total_selector, presence: true diff --git a/app/models/pipeline.rb b/app/models/pipeline.rb index d244e157..8a2fcf51 100644 --- a/app/models/pipeline.rb +++ b/app/models/pipeline.rb @@ -12,7 +12,7 @@ class Pipeline < ApplicationRecord validates :name, presence: true, uniqueness: true - def self.search(words) + def self.search(words, _format) words = sanitize_sql_like(words || '') return self if words.empty? diff --git a/app/views/pipelines/index.html.erb b/app/views/pipelines/index.html.erb index 509d4d16..aabd497c 100644 --- a/app/views/pipelines/index.html.erb +++ b/app/views/pipelines/index.html.erb @@ -9,7 +9,28 @@ <% end %> <%= form_with url: '/pipelines', method: :get, class: 'row mb-4' do |form| %> +
    +
    +
    + <%= form.text_field(:search, value: params[:search], class: 'form-control', 'aria-label': 'Search') %> + +
    +
    + + +
    <%= form.label :sort_by, 'Sort by:', class: 'col-form-label' %>
    @@ -20,16 +41,6 @@ class: 'form-select', 'data-submitting-select': true ) %>
    -
    -
    - <%= form.label :search, 'Search:', class: 'col-form-label' %> -
    -
    -
    - <%= form.text_field(:search, value: params[:search], class: 'form-control') %> - -
    -
    <% end %>
    From 3449aa4b1666918df2e8bac02fa68a10cb1c7679 Mon Sep 17 00:00:00 2001 From: Paul Mesnilgrente Date: Wed, 11 Oct 2023 01:28:01 +0200 Subject: [PATCH 09/18] feat(pipeline): ability to search by username --- app/models/pipeline.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/models/pipeline.rb b/app/models/pipeline.rb index 8a2fcf51..f65518d5 100644 --- a/app/models/pipeline.rb +++ b/app/models/pipeline.rb @@ -16,7 +16,12 @@ def self.search(words, _format) words = sanitize_sql_like(words || '') return self if words.empty? - where('name LIKE ?', "%#{words}%").or(where('description LIKE ?', "%#{words}%")) + words = "%#{words}%" + users = User.select(:id).where('username LIKE ?', words) + + where('name LIKE ?', words) + .or(where('description LIKE ?', words)) + .or(where(last_edited_by: users)) end def harvest From b04d6915c3e909a0477e68e9cfa3f1220ef05cf7 Mon Sep 17 00:00:00 2001 From: Paul Mesnilgrente Date: Wed, 11 Oct 2023 01:54:52 +0200 Subject: [PATCH 10/18] feat(pipeline): added format filter --- app/controllers/pipelines_controller.rb | 6 +----- app/models/pipeline.rb | 12 ++++++++++-- app/views/pipelines/index.html.erb | 6 ++---- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/controllers/pipelines_controller.rb b/app/controllers/pipelines_controller.rb index 3e502f77..6ac47896 100644 --- a/app/controllers/pipelines_controller.rb +++ b/app/controllers/pipelines_controller.rb @@ -78,11 +78,7 @@ def find_pipeline end def pipelines - Pipeline.search(params[:search], format).order(sort_by).page(params[:page]) - end - - def format - @format ||= params['format'] + Pipeline.search(params[:search], params[:format]).order(sort_by).page(params[:page]) end def sort_by diff --git a/app/models/pipeline.rb b/app/models/pipeline.rb index f65518d5..6f3eb862 100644 --- a/app/models/pipeline.rb +++ b/app/models/pipeline.rb @@ -12,9 +12,9 @@ class Pipeline < ApplicationRecord validates :name, presence: true, uniqueness: true - def self.search(words, _format) + def self.search(words, format) words = sanitize_sql_like(words || '') - return self if words.empty? + return by_format(format) if words.empty? words = "%#{words}%" users = User.select(:id).where('username LIKE ?', words) @@ -22,6 +22,14 @@ def self.search(words, _format) where('name LIKE ?', words) .or(where('description LIKE ?', words)) .or(where(last_edited_by: users)) + .or(by_format(format)) + end + + def self.by_format(format) + return self if format.blank? + + pipeline_ids = ExtractionDefinition.where(format:).pluck(:pipeline_id) + where(id: pipeline_ids) end def harvest diff --git a/app/views/pipelines/index.html.erb b/app/views/pipelines/index.html.erb index aabd497c..67543a46 100644 --- a/app/views/pipelines/index.html.erb +++ b/app/views/pipelines/index.html.erb @@ -17,19 +17,17 @@
    -
    <%= form.label :sort_by, 'Sort by:', class: 'col-form-label' %>
    From baff6c48c85d286ca46fa2affbfdbbf43ab58b0c Mon Sep 17 00:00:00 2001 From: Paul Mesnilgrente Date: Wed, 11 Oct 2023 02:34:48 +0200 Subject: [PATCH 11/18] feat(pipeline): added source_id filter --- app/models/pipeline.rb | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/app/models/pipeline.rb b/app/models/pipeline.rb index 6f3eb862..01baedb4 100644 --- a/app/models/pipeline.rb +++ b/app/models/pipeline.rb @@ -13,23 +13,35 @@ class Pipeline < ApplicationRecord validates :name, presence: true, uniqueness: true def self.search(words, format) + words = sanitized_words(words) + return self if words.blank? && format.blank? + + query = where('name LIKE ?', words) + .or(where('description LIKE ?', words)) + .or(where(last_edited_by_id: search_user_ids(words))) + .or(where(id: search_source_ids(words))) + + query = query.and(where(id: search_format_ids(format))) if format.present? + query + end + + def self.sanitized_words(words) words = sanitize_sql_like(words || '') - return by_format(format) if words.empty? + return nil if words.empty? - words = "%#{words}%" - users = User.select(:id).where('username LIKE ?', words) + "%#{words}%" + end - where('name LIKE ?', words) - .or(where('description LIKE ?', words)) - .or(where(last_edited_by: users)) - .or(by_format(format)) + def self.search_user_ids(words) + User.where('username LIKE ?', words).pluck(:id) end - def self.by_format(format) - return self if format.blank? + def self.search_source_ids(words) + HarvestDefinition.where('source_id LIKE ?', words).pluck(:pipeline_id) + end - pipeline_ids = ExtractionDefinition.where(format:).pluck(:pipeline_id) - where(id: pipeline_ids) + def self.search_format_ids(format) + ExtractionDefinition.where(format:).pluck(:pipeline_id) end def harvest From b7f8fb7db3aaf4348371fa35fb4cf525fef37eb7 Mon Sep 17 00:00:00 2001 From: Paul Mesnilgrente Date: Wed, 11 Oct 2023 14:53:19 +0200 Subject: [PATCH 12/18] feat(pipeline): added button to clear search --- app/frontend/entrypoints/application.js | 1 + app/frontend/entrypoints/application.scss | 6 +-- app/frontend/js/ClearField.js | 14 +++++ app/frontend/stylesheets/blocks/_search.scss | 51 +++++++++++++++++++ app/frontend/stylesheets/modules/_colors.scss | 9 ++-- app/views/pipelines/index.html.erb | 30 ++++++++--- 6 files changed, 96 insertions(+), 15 deletions(-) create mode 100644 app/frontend/js/ClearField.js create mode 100644 app/frontend/stylesheets/blocks/_search.scss diff --git a/app/frontend/entrypoints/application.js b/app/frontend/entrypoints/application.js index 41c259fc..35c48ae7 100644 --- a/app/frontend/entrypoints/application.js +++ b/app/frontend/entrypoints/application.js @@ -31,6 +31,7 @@ console.log( // import '~/index.css' import * as bootstrap from "bootstrap"; +import "/js/ClearField"; import "/js/TestRecordExtraction"; import "/js/TestEnrichmentExtraction"; import "/js/TestTransformationRecordSelector"; diff --git a/app/frontend/entrypoints/application.scss b/app/frontend/entrypoints/application.scss index 21e1e932..2e954086 100644 --- a/app/frontend/entrypoints/application.scss +++ b/app/frontend/entrypoints/application.scss @@ -1,16 +1,13 @@ // Modules - @import "../stylesheets/modules/colors"; @import "../stylesheets/modules/fonts"; // Libraries - @import "~bootstrap/scss/bootstrap"; @import "~bootstrap-icons/font/bootstrap-icons.min"; @import "~autoComplete/dist/css/autoComplete.css"; // Mixins - @import "../stylesheets/mixins/bem"; // Bootstrap overrides @@ -25,12 +22,11 @@ @import "../stylesheets/bootstrap/accordion"; // Library overrides - @import "../stylesheets/autocomplete/autocomplete"; - // Blocks @import "../stylesheets/blocks/card-link"; +@import "../stylesheets/blocks/search"; @import "../stylesheets/blocks/field-nav"; @import "../stylesheets/blocks/field-nav-panel"; @import "../stylesheets/blocks/form-in-dropdown"; diff --git a/app/frontend/js/ClearField.js b/app/frontend/js/ClearField.js new file mode 100644 index 00000000..1ce25295 --- /dev/null +++ b/app/frontend/js/ClearField.js @@ -0,0 +1,14 @@ +const clearButtons = document.querySelectorAll("[data-clear-field]"); + +clearButtons.forEach(function (clearButton) { + clearButton.addEventListener("click", (event) => { + const form = event.target.closest("form"); + const fieldNameToClear = event.target.dataset.clearField; + const fieldToClear = form.querySelector( + `input[name="${fieldNameToClear}"]` + ); + + fieldToClear.value = ""; + form.submit(); + }); +}); diff --git a/app/frontend/stylesheets/blocks/_search.scss b/app/frontend/stylesheets/blocks/_search.scss new file mode 100644 index 00000000..616e463b --- /dev/null +++ b/app/frontend/stylesheets/blocks/_search.scss @@ -0,0 +1,51 @@ +.search { + position: relative; + + @include element('label') { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + padding: .4rem $form-floating-padding-x; + overflow: hidden; + text-align: start; + text-overflow: ellipsis; + white-space: nowrap; + pointer-events: none; + border: 1px solid transparent; + } + + &__input { + width: 22rem; + + &::placeholder { + color: transparent; + } + + &:focus ~ .search__label, &:not(:placeholder-shown) ~ .search__label { + display: none; + } + } + + @include element('clear') { + position: absolute; + top: 0; + right: .5rem; + margin-top: .3rem; + + --bs-btn-padding-y: .25rem; + --bs-btn-padding-x: .5rem; + --bs-btn-font-size: .75rem; + --bs-btn-border-color: #{$lynx-white}; + --bs-btn-bg: #{$lynx-white}; + --bs-btn-color: #{$primary}; + --bs-btn-hover-color: #{$primary}; + --bs-btn-hover-bg: #{shade-color($lynx-white, 10%)}; + --bs-btn-hover-border-color: #{shade-color($lynx-white, 10%)}; + + > .bi-plus::before { + transform: rotate(45deg); + } + } +} diff --git a/app/frontend/stylesheets/modules/_colors.scss b/app/frontend/stylesheets/modules/_colors.scss index a2070f3d..37a0132d 100644 --- a/app/frontend/stylesheets/modules/_colors.scss +++ b/app/frontend/stylesheets/modules/_colors.scss @@ -1,12 +1,13 @@ -// https://color-name-generator.com/ +// https://color-name-generator.com/ $doctor: #F8F9FA; $bleached-silk: #F2F2F2; $vital-green: #198754; $antarctic-deep: #343A40; -$bright-star: #dee2e6; -$pompeii-ash: #6c757d; -$satin-white: #ced4da; +$bright-star: #DEE2E6; +$pompeii-ash: #6C757D; +$satin-white: #CED4DA; +$lynx-white: #F7F7F7; $primary: $vital-green; $dark: $antarctic-deep; diff --git a/app/views/pipelines/index.html.erb b/app/views/pipelines/index.html.erb index 67543a46..4cd04d92 100644 --- a/app/views/pipelines/index.html.erb +++ b/app/views/pipelines/index.html.erb @@ -8,14 +8,31 @@
    <% end %> -<%= form_with url: '/pipelines', method: :get, class: 'row mb-4' do |form| %> -
    -
    -
    - <%= form.text_field(:search, value: params[:search], class: 'form-control', 'aria-label': 'Search') %> - +<%= form_with url: '/pipelines', method: :get, class: 'row justify-content-end mb-5' do |form| %> +
    +
    +
    + +
    <%= form.label :format, 'Format:', class: 'col-form-label' %> @@ -28,6 +45,7 @@ class: 'form-select', 'data-submitting-select': true ) %>
    +
    <%= form.label :sort_by, 'Sort by:', class: 'col-form-label' %>
    From 8da049b3b6a542cdf98508cabb153bef4fda9c49 Mon Sep 17 00:00:00 2001 From: Paul Mesnilgrente Date: Wed, 11 Oct 2023 15:20:09 +0200 Subject: [PATCH 13/18] refactor(pipeline): put the search query in a different module --- app/controllers/pipelines_controller.rb | 2 +- app/models/pipeline.rb | 19 --------- app/queries/pipeline_search_query.rb | 51 +++++++++++++++++++++++++ config/application.rb | 1 + 4 files changed, 53 insertions(+), 20 deletions(-) create mode 100644 app/queries/pipeline_search_query.rb diff --git a/app/controllers/pipelines_controller.rb b/app/controllers/pipelines_controller.rb index 6ac47896..462b480f 100644 --- a/app/controllers/pipelines_controller.rb +++ b/app/controllers/pipelines_controller.rb @@ -78,7 +78,7 @@ def find_pipeline end def pipelines - Pipeline.search(params[:search], params[:format]).order(sort_by).page(params[:page]) + PipelineSearchQuery.new(params).call.order(sort_by).page(params[:page]) end def sort_by diff --git a/app/models/pipeline.rb b/app/models/pipeline.rb index 01baedb4..9b554f2e 100644 --- a/app/models/pipeline.rb +++ b/app/models/pipeline.rb @@ -25,25 +25,6 @@ def self.search(words, format) query end - def self.sanitized_words(words) - words = sanitize_sql_like(words || '') - return nil if words.empty? - - "%#{words}%" - end - - def self.search_user_ids(words) - User.where('username LIKE ?', words).pluck(:id) - end - - def self.search_source_ids(words) - HarvestDefinition.where('source_id LIKE ?', words).pluck(:pipeline_id) - end - - def self.search_format_ids(format) - ExtractionDefinition.where(format:).pluck(:pipeline_id) - end - def harvest harvest_definitions.find_by(kind: 'harvest') end diff --git a/app/queries/pipeline_search_query.rb b/app/queries/pipeline_search_query.rb new file mode 100644 index 00000000..d984dc5f --- /dev/null +++ b/app/queries/pipeline_search_query.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +class PipelineSearchQuery + def initialize(params) + @words = sanitized_words(params[:search]) + @format = params[:format] + @query = Pipeline + end + + def call + return @query if @words.blank? && @format.blank? + return @query.where(id: search_format_ids) if @words.blank? && @format.present? + + @query = or_words_filters + @query = and_format_filter if @format.present? + + @query + end + + private + + def sanitized_words(words) + words = Pipeline.sanitize_sql_like(words || '') + return nil if words.empty? + + "%#{words}%" + end + + def or_words_filters + @query.where('name LIKE ?', @words) + .or(Pipeline.where('description LIKE ?', @words)) + .or(Pipeline.where(last_edited_by_id: search_user_ids)) + .or(Pipeline.where(id: search_source_ids)) + end + + def and_format_filter + @query.and(Pipeline.where(id: search_format_ids)) + end + + def search_user_ids + User.where('username LIKE ?', @words).pluck(:id) + end + + def search_source_ids + HarvestDefinition.where('source_id LIKE ?', @words).pluck(:pipeline_id) + end + + def search_format_ids + ExtractionDefinition.where(format: @format).pluck(:pipeline_id) + end +end diff --git a/config/application.rb b/config/application.rb index f7d443e1..3e037e45 100644 --- a/config/application.rb +++ b/config/application.rb @@ -34,6 +34,7 @@ class Application < Rails::Application # config.eager_load_paths << Rails.root.join("extras") config.eager_load_paths << Rails.root.join('supplejack') config.eager_load_paths << Rails.root.join('slices') + config.eager_load_paths << Rails.root.join('queries') config.time_zone = ENV.fetch('TIME_ZONE', 'Auckland') From 25ed22f4597a4c39242867dac22f1cc80fb0062f Mon Sep 17 00:00:00 2001 From: Paul Mesnilgrente Date: Wed, 11 Oct 2023 16:04:27 +0200 Subject: [PATCH 14/18] feat(harvest): updated card__link style... the whole card is clickable so we don't need the a style on the title --- .../stylesheets/blocks/_card-link.scss | 19 ++++++++++++------- app/frontend/stylesheets/bootstrap/_card.scss | 2 +- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/app/frontend/stylesheets/blocks/_card-link.scss b/app/frontend/stylesheets/blocks/_card-link.scss index 5673553f..2bfc91d4 100644 --- a/app/frontend/stylesheets/blocks/_card-link.scss +++ b/app/frontend/stylesheets/blocks/_card-link.scss @@ -6,12 +6,17 @@ border-color: $primary; } - .card__link::after { - content: ""; - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; + .card__link { + color: var(--bs-card-color); + text-decoration: none; + + &::after { + content: ""; + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; + } } } diff --git a/app/frontend/stylesheets/bootstrap/_card.scss b/app/frontend/stylesheets/bootstrap/_card.scss index be3ea155..8d74402c 100644 --- a/app/frontend/stylesheets/bootstrap/_card.scss +++ b/app/frontend/stylesheets/bootstrap/_card.scss @@ -34,7 +34,7 @@ } @include element('control-action') { - padding: 10px 16px 9px 16px; + padding: .625rem 1rem; } @include element('control') { From 1c72bd3abf7384fbba188d79b55be79b77ec32fa Mon Sep 17 00:00:00 2001 From: Paul Mesnilgrente Date: Wed, 11 Oct 2023 16:13:22 +0200 Subject: [PATCH 15/18] refactor(harvest): moved the harvest card to its own block --- app/frontend/entrypoints/application.scss | 5 ++-- .../stylesheets/blocks/_harvest-card.scss | 29 +++++++++++++++++++ app/frontend/stylesheets/bootstrap/_card.scss | 28 ------------------ app/views/pipelines/_card.html.erb | 20 ++++++------- 4 files changed, 42 insertions(+), 40 deletions(-) create mode 100644 app/frontend/stylesheets/blocks/_harvest-card.scss diff --git a/app/frontend/entrypoints/application.scss b/app/frontend/entrypoints/application.scss index 2e954086..6147ac61 100644 --- a/app/frontend/entrypoints/application.scss +++ b/app/frontend/entrypoints/application.scss @@ -26,12 +26,13 @@ // Blocks @import "../stylesheets/blocks/card-link"; -@import "../stylesheets/blocks/search"; +@import "../stylesheets/blocks/definition-group"; @import "../stylesheets/blocks/field-nav"; @import "../stylesheets/blocks/field-nav-panel"; @import "../stylesheets/blocks/form-in-dropdown"; +@import "../stylesheets/blocks/harvest-card"; @import "../stylesheets/blocks/header"; @import "../stylesheets/blocks/jump-to"; @import "../stylesheets/blocks/record-view"; -@import "../stylesheets/blocks/definition-group"; +@import "../stylesheets/blocks/search"; @import "../stylesheets/blocks/table"; diff --git a/app/frontend/stylesheets/blocks/_harvest-card.scss b/app/frontend/stylesheets/blocks/_harvest-card.scss new file mode 100644 index 00000000..4a40c0d3 --- /dev/null +++ b/app/frontend/stylesheets/blocks/_harvest-card.scss @@ -0,0 +1,29 @@ +.harvest-card { + @include element('control') { + position: absolute; + right: .5rem; + top: 0; + bottom: 0; + margin: auto; + height: 1rem; + + &:hover { + cursor: pointer; + } + } + + @include element('control-action') { + padding: .625rem 1rem; + } + + @include element('right-arrow') { + position: absolute; + right: -1.5rem; + top: 0; + bottom: 0; + width: 1.25rem; + height: 1.25rem; + margin: auto; + color: $primary; + } +} diff --git a/app/frontend/stylesheets/bootstrap/_card.scss b/app/frontend/stylesheets/bootstrap/_card.scss index 8d74402c..ff242997 100644 --- a/app/frontend/stylesheets/bootstrap/_card.scss +++ b/app/frontend/stylesheets/bootstrap/_card.scss @@ -32,34 +32,6 @@ cursor: default } } - - @include element('control-action') { - padding: .625rem 1rem; - } - - @include element('control') { - position: absolute; - right: .5rem; - top: 0; - bottom: 0; - margin: auto; - height: 1rem; - - &:hover { - cursor: pointer; - } - } - - @include element('right-arrow') { - position: absolute; - right: -1.5rem; - top: 0; - bottom: 0; - width: 1.25rem; - height: 1.25rem; - margin: auto; - color: $primary; - } } form { diff --git a/app/views/pipelines/_card.html.erb b/app/views/pipelines/_card.html.erb index 4bff475a..d06a0b8a 100644 --- a/app/views/pipelines/_card.html.erb +++ b/app/views/pipelines/_card.html.erb @@ -5,7 +5,7 @@ delete_path ||= '' position = type == 'extraction' ? 'first' : 'last' %> -
    +

    <%= link_to edit_path, class: 'card__link' do %> @@ -20,11 +20,11 @@ Shared (<%= "#{definition.harvest_definitions.count} pipelines" %>) <% end %> - From 0cc08ec5799a28c56a769ce7d3e28c3d7bd95730 Mon Sep 17 00:00:00 2001 From: Paul Mesnilgrente Date: Wed, 11 Oct 2023 16:21:03 +0200 Subject: [PATCH 16/18] feat(harvest): used rows and columns... that's to separate clearly the content from the controls --- app/views/pipelines/_card.html.erb | 145 +++++++++++++++-------------- 1 file changed, 76 insertions(+), 69 deletions(-) diff --git a/app/views/pipelines/_card.html.erb b/app/views/pipelines/_card.html.erb index d06a0b8a..c28c6a22 100644 --- a/app/views/pipelines/_card.html.erb +++ b/app/views/pipelines/_card.html.erb @@ -7,85 +7,92 @@
    -

    - <%= link_to edit_path, class: 'card__link' do %> - <%= definition.name %> - <% end %> -

    -

    - <%= send("#{type}_card_subtitle", definition) %> -

    - - <% if definition.shared? %> - Shared (<%= "#{definition.harvest_definitions.count} pipelines" %>) - <% end %> - -
    - - +
    +
    +

    <% if position == 'first' %> From d33c3cfa76cd5cccac509cf654bf997b19124496 Mon Sep 17 00:00:00 2001 From: Paul Mesnilgrente Date: Wed, 11 Oct 2023 16:41:05 +0200 Subject: [PATCH 17/18] feat(harvest): enlarged the actions toggle button --- .../stylesheets/blocks/_harvest-card.scss | 12 +++++++---- app/views/pipelines/_card.html.erb | 20 ++++++++++--------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/app/frontend/stylesheets/blocks/_harvest-card.scss b/app/frontend/stylesheets/blocks/_harvest-card.scss index 4a40c0d3..3879f3ff 100644 --- a/app/frontend/stylesheets/blocks/_harvest-card.scss +++ b/app/frontend/stylesheets/blocks/_harvest-card.scss @@ -1,18 +1,22 @@ .harvest-card { - @include element('control') { + @include element('actions') { position: absolute; - right: .5rem; + right: 0rem; top: 0; bottom: 0; margin: auto; - height: 1rem; + height: 2.25rem; &:hover { cursor: pointer; } } - @include element('control-action') { + @include element('actions-toggle') { + border: none; + } + + @include element('action') { padding: .625rem 1rem; } diff --git a/app/views/pipelines/_card.html.erb b/app/views/pipelines/_card.html.erb index c28c6a22..c8326172 100644 --- a/app/views/pipelines/_card.html.erb +++ b/app/views/pipelines/_card.html.erb @@ -24,11 +24,13 @@
    -
    - +