spina-admin-journal 0.6.1 → 1.0.0.rc3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -48
  3. data/app/assets/config/spina_admin_journal_manifest.js +1 -2
  4. data/app/assets/javascripts/spina/admin/journal/application.js +0 -0
  5. data/app/components/spina/admin/journal/affiliations_list_component.rb +32 -0
  6. data/app/components/spina/admin/journal/application_component.rb +11 -0
  7. data/app/components/spina/admin/journal/articles_list_component.rb +54 -0
  8. data/app/components/spina/admin/journal/authors_list_component.rb +37 -0
  9. data/app/components/spina/admin/journal/authorships_list_component.rb +45 -0
  10. data/app/components/spina/admin/journal/empty_list_component.html.haml +1 -0
  11. data/app/components/spina/admin/journal/empty_list_component.rb +14 -0
  12. data/app/components/spina/admin/journal/form_group_component.html.haml +4 -0
  13. data/app/components/spina/admin/journal/form_group_component.rb +17 -0
  14. data/app/components/spina/admin/journal/institutions_list_component.rb +32 -0
  15. data/app/components/spina/admin/journal/issues_list_component.rb +47 -0
  16. data/app/components/spina/admin/journal/licences_list_component.rb +32 -0
  17. data/app/components/spina/admin/journal/list_component.html.haml +11 -0
  18. data/app/components/spina/admin/journal/list_component.rb +22 -0
  19. data/app/components/spina/admin/journal/list_item_component.html.haml +17 -0
  20. data/app/components/spina/admin/journal/list_item_component.rb +23 -0
  21. data/app/components/spina/admin/journal/volumes_list_component.rb +34 -0
  22. data/app/controllers/spina/admin/journal/application_controller.rb +1 -1
  23. data/app/controllers/spina/admin/journal/articles_controller.rb +18 -24
  24. data/app/controllers/spina/admin/journal/authors_controller.rb +18 -26
  25. data/app/controllers/spina/admin/journal/institutions_controller.rb +13 -5
  26. data/app/controllers/spina/admin/journal/issues_controller.rb +20 -27
  27. data/app/controllers/spina/admin/journal/journals_controller.rb +4 -1
  28. data/app/controllers/spina/admin/journal/licences_controller.rb +8 -4
  29. data/app/controllers/spina/admin/journal/volumes_controller.rb +16 -21
  30. data/app/models/spina/admin/journal/author.rb +1 -0
  31. data/app/models/spina/admin/journal/authorship.rb +9 -0
  32. data/app/views/spina/admin/hooks/journal/_primary_navigation.html.haml +35 -26
  33. data/app/views/spina/admin/journal/articles/_form.html.haml +23 -23
  34. data/app/views/spina/admin/journal/articles/_form_authors.html.haml +3 -15
  35. data/app/views/spina/admin/journal/articles/_form_details.html.haml +37 -56
  36. data/app/views/spina/admin/journal/articles/index.html.haml +7 -17
  37. data/app/views/spina/admin/journal/articles/view_authors.html.haml +2 -0
  38. data/app/views/spina/admin/journal/authors/_form.html.haml +24 -24
  39. data/app/views/spina/admin/journal/authors/_form_affiliation.html.haml +12 -20
  40. data/app/views/spina/admin/journal/authors/_form_articles.html.haml +3 -18
  41. data/app/views/spina/admin/journal/authors/_form_details.html.haml +27 -5
  42. data/app/views/spina/admin/journal/authors/index.html.haml +7 -15
  43. data/app/views/spina/admin/journal/authors/view_articles.html.haml +2 -0
  44. data/app/views/spina/admin/journal/institutions/_form.html.haml +24 -24
  45. data/app/views/spina/admin/journal/institutions/_form_details.html.haml +3 -8
  46. data/app/views/spina/admin/journal/institutions/_form_view_affiliations.html.haml +3 -10
  47. data/app/views/spina/admin/journal/institutions/index.html.haml +7 -15
  48. data/app/views/spina/admin/journal/institutions/view_affiliations.html.haml +2 -0
  49. data/app/views/spina/admin/journal/issues/_form.html.haml +23 -23
  50. data/app/views/spina/admin/journal/issues/_form_articles.html.haml +3 -14
  51. data/app/views/spina/admin/journal/issues/_form_details.html.haml +14 -28
  52. data/app/views/spina/admin/journal/issues/index.html.haml +7 -16
  53. data/app/views/spina/admin/journal/issues/view_articles.html.haml +3 -0
  54. data/app/views/spina/admin/journal/journals/_form.html.haml +22 -30
  55. data/app/views/spina/admin/journal/licences/_form.html.haml +24 -37
  56. data/app/views/spina/admin/journal/licences/index.html.haml +7 -16
  57. data/app/views/spina/admin/journal/volumes/_form.html.haml +25 -12
  58. data/app/views/spina/admin/journal/volumes/_form_details.html.haml +5 -10
  59. data/app/views/spina/admin/journal/volumes/_form_issues.html.haml +2 -13
  60. data/app/views/spina/admin/journal/volumes/index.html.haml +7 -16
  61. data/app/views/spina/admin/journal/volumes/view_issues.html.haml +2 -0
  62. data/app/views/spina/admin/parts/admin/journal/page_ranges/_form.html.haml +3 -4
  63. data/config/locales/en.yml +70 -8
  64. data/config/routes.rb +26 -6
  65. data/db/migrate/20220130171603_add_orcid_to_spina_admin_journal_author.rb +7 -0
  66. data/lib/spina/admin/journal/engine.rb +12 -0
  67. data/lib/spina/admin/journal/version.rb +1 -1
  68. data/lib/spina/admin/journal.rb +1 -0
  69. metadata +55 -65
  70. data/app/assets/javascripts/spina/admin/journal/application.es6 +0 -72
  71. data/app/views/layouts/spina/admin/journal/articles.html.haml +0 -10
  72. data/app/views/layouts/spina/admin/journal/authors.html.haml +0 -10
  73. data/app/views/layouts/spina/admin/journal/institutions.html.haml +0 -10
  74. data/app/views/layouts/spina/admin/journal/issues.html.haml +0 -10
  75. data/app/views/layouts/spina/admin/journal/journals.html.haml +0 -10
  76. data/app/views/layouts/spina/admin/journal/licences.html.haml +0 -10
  77. data/app/views/layouts/spina/admin/journal/volumes.html.haml +0 -10
  78. data/app/views/spina/admin/journal/affiliations/_affiliation.html.haml +0 -8
  79. data/app/views/spina/admin/journal/application/_empty_list.html.haml +0 -3
  80. data/app/views/spina/admin/journal/articles/_article.html.haml +0 -10
  81. data/app/views/spina/admin/journal/authors/_author.html.haml +0 -8
  82. data/app/views/spina/admin/journal/authorships/_authorship.html.haml +0 -9
  83. data/app/views/spina/admin/journal/institutions/_institution.html.haml +0 -9
  84. data/app/views/spina/admin/journal/issues/_issue.html.haml +0 -9
  85. data/app/views/spina/admin/journal/journals/index.html.haml +0 -27
  86. data/app/views/spina/admin/journal/licences/_licence.html.haml +0 -11
  87. data/app/views/spina/admin/journal/volumes/_volume.html.haml +0 -7
  88. data/vendor/assets/javascripts/spina/admin/journal/html5sortable.js +0 -1295
@@ -25,10 +25,12 @@ module Spina
25
25
 
26
26
  before_action :set_breadcrumb
27
27
  before_action :set_tabs, except: %i[index destroy sort]
28
- before_action :set_article, only: %i[edit update destroy]
28
+ before_action :set_article, only: %i[edit view_authors update destroy]
29
29
  before_action :set_parts_attributes, only: %i[new edit]
30
30
  before_action :build_parts, only: %i[edit]
31
31
 
32
+ admin_section :journal
33
+
32
34
  def index
33
35
  @articles = Article.sorted_desc
34
36
  end
@@ -41,23 +43,29 @@ module Spina
41
43
 
42
44
  def edit; end
43
45
 
44
- def create
46
+ def view_authors
47
+ render layout: false
48
+ end
49
+
50
+ def create # rubocop:disable Metrics/AbcSize
45
51
  @article = Article.new(article_params)
46
52
  sister_articles = Article.where(issue: @article.issue_id)
47
53
  @article.number = sister_articles.any? ? sister_articles.sorted_desc.first.number + 1 : 1
48
54
 
49
55
  if @article.save
50
- redirect_to admin_journal_articles_path, success: t('.saved')
56
+ redirect_to edit_admin_journal_article_path(@article), success: t('.saved')
51
57
  else
52
- render :new
58
+ flash.now[:alert] = t('.failed')
59
+ render :new, status: :unprocessable_entity
53
60
  end
54
61
  end
55
62
 
56
63
  def update
57
64
  if @article.update(article_params)
58
- redirect_to admin_journal_articles_path, success: t('.saved')
65
+ redirect_to edit_admin_journal_article_path(@article), success: t('.saved')
59
66
  else
60
- render :edit
67
+ flash.now[:alert] = t('.failed')
68
+ render :edit, status: :unprocessable_entity
61
69
  end
62
70
  end
63
71
 
@@ -71,15 +79,11 @@ module Spina
71
79
  end
72
80
 
73
81
  def sort
74
- ActiveRecord::Base.transaction do
75
- sort_params.each do |id, new_pos|
76
- Article.find(id.to_i).update_attribute(:number, new_pos.to_i) # rubocop:disable Rails/SkipsModelValidations
77
- end
78
- validate_sort_order
82
+ params[:ids].each.with_index do |id, index|
83
+ Article.where(id: id).update_all(number: index + 1) # rubocop:disable Rails/SkipsModelValidations
79
84
  end
80
- render json: { success: true, message: t('.sort_success') }
81
- rescue ActiveRecord::RecordInvalid
82
- render json: { success: false, message: t('.sort_error') }
85
+ flash.now[:info] = t('spina.pages.sorting_saved')
86
+ render_flash
83
87
  end
84
88
 
85
89
  private
@@ -88,16 +92,6 @@ module Spina
88
92
  params.require(:article).permit(PARAMS)
89
93
  end
90
94
 
91
- def sort_params
92
- params.require(:admin_journal_articles).require(:list).permit!
93
- end
94
-
95
- def validate_sort_order
96
- Article.where(issue_id: params[:issue_id]).each do |article|
97
- raise ActiveRecord::RecordInvalid if article.invalid?
98
- end
99
- end
100
-
101
95
  def set_breadcrumb
102
96
  add_breadcrumb Article.model_name.human(count: :many), admin_journal_articles_path
103
97
  end
@@ -7,7 +7,9 @@ module Spina
7
7
  class AuthorsController < ApplicationController
8
8
  before_action :set_breadcrumb
9
9
  before_action :set_tabs, except: %i[index destroy]
10
- before_action :set_author, only: %i[edit update destroy]
10
+ before_action :set_author, only: %i[edit view_articles update destroy]
11
+
12
+ admin_section :journal_settings
11
13
 
12
14
  def index
13
15
  @authors = Author.all
@@ -21,20 +23,26 @@ module Spina
21
23
 
22
24
  def edit; end
23
25
 
26
+ def view_articles
27
+ render layout: false
28
+ end
29
+
24
30
  def create
25
31
  @author = Author.new(modified_params)
26
32
  if @author.save
27
- redirect_to admin_journal_authors_path, success: t('.saved')
33
+ redirect_to edit_admin_journal_author_path(@author), success: t('.saved')
28
34
  else
29
- render :new
35
+ flash.now[:alert] = t('.failed')
36
+ render :new, status: :unprocessable_entity
30
37
  end
31
38
  end
32
39
 
33
40
  def update
34
41
  if @author.update(modified_params)
35
- redirect_to admin_journal_authors_path, success: t('.saved')
42
+ redirect_to edit_admin_journal_author_path(@author), success: t('.saved')
36
43
  else
37
- render :edit
44
+ flash.now[:alert] = t('.failed')
45
+ render :edit, status: :unprocessable_entity
38
46
  end
39
47
  end
40
48
 
@@ -48,23 +56,17 @@ module Spina
48
56
  end
49
57
 
50
58
  def sort
51
- ActiveRecord::Base.transaction do
52
- sort_params.each do |id, new_pos|
53
- # ignore uniqueness validation for now
54
- Authorship.find(id.to_i).update_attribute(:position, new_pos.to_i) # rubocop:disable Rails/SkipsModelValidations
55
- end
56
- # do validations after reordering is complete
57
- validate_sort_order
59
+ params[:ids].each.with_index do |id, index|
60
+ Authorship.where(id: id).update_all(position: index + 1) # rubocop:disable Rails/SkipsModelValidations
58
61
  end
59
- render json: { success: true, message: t('.sort_success') }
60
- rescue ActiveRecord::RecordInvalid
61
- render json: { success: false, message: t('.sort_error') }
62
+ flash.now[:info] = t('spina.pages.sorting_saved')
63
+ render_flash
62
64
  end
63
65
 
64
66
  private
65
67
 
66
68
  def author_params
67
- params.require(:author).permit(:primary_affiliation_index,
69
+ params.require(:author).permit(:primary_affiliation_index, :orcid,
68
70
  affiliations_attributes: %i[id institution_id first_name
69
71
  surname])
70
72
  end
@@ -80,10 +82,6 @@ module Spina
80
82
  new_params
81
83
  end
82
84
 
83
- def sort_params
84
- params.require(:admin_journal_authorships).require(:list).permit!
85
- end
86
-
87
85
  def set_breadcrumb
88
86
  add_breadcrumb Author.model_name.human(count: :many), admin_journal_authors_path
89
87
  end
@@ -96,12 +94,6 @@ module Spina
96
94
  @author = Author.find(params[:id])
97
95
  add_breadcrumb @author.primary_affiliation.name
98
96
  end
99
-
100
- def validate_sort_order
101
- Authorship.where(article_id: params[:article_id]).each do |authorship|
102
- raise ActiveRecord::RecordInvalid if authorship.invalid?
103
- end
104
- end
105
97
  end
106
98
  end
107
99
  end
@@ -7,7 +7,9 @@ module Spina
7
7
  class InstitutionsController < ApplicationController
8
8
  before_action :set_breadcrumb
9
9
  before_action :set_tabs, except: %i[index destroy]
10
- before_action :set_institution, only: %i[edit update destroy]
10
+ before_action :set_institution, only: %i[edit view_affiliations update destroy]
11
+
12
+ admin_section :journal_settings
11
13
 
12
14
  def index
13
15
  @institutions = Institution.all
@@ -20,21 +22,27 @@ module Spina
20
22
 
21
23
  def edit; end
22
24
 
25
+ def view_affiliations
26
+ render layout: false
27
+ end
28
+
23
29
  def create
24
30
  @institution = Institution.new(institution_params)
25
31
 
26
32
  if @institution.save
27
- redirect_to admin_journal_institutions_path, success: t('.saved')
33
+ redirect_to edit_admin_journal_institution_path(@institution), success: t('.saved')
28
34
  else
29
- render :new
35
+ flash.now[:alert] = t('.failed')
36
+ render :new, status: :unprocessable_entity
30
37
  end
31
38
  end
32
39
 
33
40
  def update
34
41
  if @institution.update(institution_params)
35
- redirect_to admin_journal_institutions_path, success: t('.saved')
42
+ redirect_to edit_admin_journal_institution_path(@institution), success: t('.saved')
36
43
  else
37
- render :edit
44
+ flash.now[:alert] = t('.failed')
45
+ render :edit, status: :unprocessable_entity
38
46
  end
39
47
  end
40
48
 
@@ -5,7 +5,7 @@ module Spina
5
5
  module Journal
6
6
  # Controller for {Issue} records.
7
7
  # TODO: extract methods to helpers to reduce class length
8
- class IssuesController < ApplicationController # rubocop:disable Metrics/ClassLength
8
+ class IssuesController < ApplicationController
9
9
  PARTS_PARAMS = [
10
10
  :name, :title, :type, :content, :filename, :signed_blob_id, :alt, :attachment_id, :image_id,
11
11
  { images_attributes: %i[filename signed_blob_id image_id alt],
@@ -25,11 +25,13 @@ module Spina
25
25
 
26
26
  before_action :set_breadcrumb
27
27
  before_action :set_tabs, except: %i[index destroy sort]
28
- before_action :set_issue, only: %i[edit update destroy]
29
- before_action :set_articles, only: %i[edit update]
28
+ before_action :set_issue, only: %i[edit view_articles update destroy]
29
+ before_action :set_articles, only: %i[edit view_articles update]
30
30
  before_action :set_parts_attributes, only: %i[new edit]
31
31
  before_action :build_parts, only: %i[edit]
32
32
 
33
+ admin_section :journal
34
+
33
35
  def index
34
36
  @issues = Issue.sorted_desc
35
37
  end
@@ -46,23 +48,29 @@ module Spina
46
48
  add_breadcrumb t('spina.admin.journal.issues.issue_number', number: @issue.number)
47
49
  end
48
50
 
49
- def create
51
+ def view_articles
52
+ render layout: false
53
+ end
54
+
55
+ def create # rubocop:disable Metrics/AbcSize
50
56
  @issue = Issue.new(issue_params)
51
57
  sister_issues = Issue.where(volume: @issue.volume_id)
52
58
  @issue.number = sister_issues.any? ? sister_issues.sorted_desc.first.number + 1 : 1
53
59
 
54
60
  if @issue.save
55
- redirect_to admin_journal_issues_path, success: t('.saved')
61
+ redirect_to edit_admin_journal_issue_path(@issue), success: t('.saved')
56
62
  else
57
- render :new
63
+ flash.now[:alert] = t('.failed')
64
+ render :new, status: :unprocessable_entity
58
65
  end
59
66
  end
60
67
 
61
68
  def update
62
69
  if @issue.update(issue_params)
63
- redirect_to admin_journal_issues_path, success: t('.saved')
70
+ redirect_to edit_admin_journal_issue_path(@issue), success: t('.saved')
64
71
  else
65
- render :edit
72
+ flash.now[:alert] = t('.failed')
73
+ render :edit, status: :unprocessable_entity
66
74
  end
67
75
  end
68
76
 
@@ -76,16 +84,11 @@ module Spina
76
84
  end
77
85
 
78
86
  def sort
79
- ActiveRecord::Base.transaction do
80
- sort_params.each do |id, new_pos|
81
- # ignore uniqueness validation for now
82
- Issue.find(id.to_i).update_attribute(:number, new_pos.to_i) # rubocop:disable Rails/SkipsModelValidations
83
- end
84
- validate_sort_order
87
+ params[:ids].each.with_index do |id, index|
88
+ Issue.where(id: id).update_all(number: index + 1) # rubocop:disable Rails/SkipsModelValidations
85
89
  end
86
- render json: { success: true, message: t('.sort_success') }
87
- rescue ActiveRecord::RecordInvalid
88
- render json: { success: false, message: t('.sort_error') }
90
+ flash.now[:info] = t('spina.pages.sorting_saved')
91
+ render_flash
89
92
  end
90
93
 
91
94
  private
@@ -102,16 +105,6 @@ module Spina
102
105
  params.require(:issue).permit(PARAMS)
103
106
  end
104
107
 
105
- def sort_params
106
- params.require(:admin_journal_issues).require(:list).permit!
107
- end
108
-
109
- def validate_sort_order
110
- Issue.where(volume_id: params[:volume_id]).each do |issue|
111
- raise ActiveRecord::RecordInvalid if issue.invalid?
112
- end
113
- end
114
-
115
108
  def set_breadcrumb
116
109
  add_breadcrumb Issue.model_name.human(count: :many), admin_journal_issues_path
117
110
  end
@@ -27,6 +27,8 @@ module Spina
27
27
  before_action :set_parts_attributes
28
28
  before_action :build_parts, only: %i[edit]
29
29
 
30
+ admin_section :journal_settings
31
+
30
32
  def edit
31
33
  add_breadcrumb @journal.name
32
34
  end
@@ -35,7 +37,8 @@ module Spina
35
37
  if @journal.update(journal_params)
36
38
  redirect_to edit_admin_journal_journal_path(@journal), success: t('.saved')
37
39
  else
38
- render :edit
40
+ flash.now[:alert] = t('.failed')
41
+ render :edit, status: :unprocessable_entity
39
42
  end
40
43
  end
41
44
 
@@ -27,6 +27,8 @@ module Spina
27
27
  before_action :set_parts_attributes, only: %i[new edit]
28
28
  before_action :build_parts, only: %i[edit]
29
29
 
30
+ admin_section :journal_settings
31
+
30
32
  def index
31
33
  @licences = Licence.sorted
32
34
  end
@@ -42,17 +44,19 @@ module Spina
42
44
  def create
43
45
  @licence = Licence.new(licence_params)
44
46
  if @licence.save
45
- redirect_to admin_journal_licences_path, success: t('.saved')
47
+ redirect_to edit_admin_journal_licence_path(@licence), success: t('.saved')
46
48
  else
47
- render :new
49
+ flash.now[:alert] = t('.failed')
50
+ render :new, status: :unprocessable_entity
48
51
  end
49
52
  end
50
53
 
51
54
  def update
52
55
  if @licence.update(licence_params)
53
- redirect_to admin_journal_licences_path, success: t('.saved')
56
+ redirect_to edit_admin_journal_licence_path(@licence), success: t('.saved')
54
57
  else
55
- render :edit
58
+ flash.now[:alert] = t('.failed')
59
+ render :edit, status: :unprocessable_entity
56
60
  end
57
61
  end
58
62
 
@@ -7,7 +7,9 @@ module Spina
7
7
  class VolumesController < ApplicationController
8
8
  before_action :set_breadcrumb
9
9
  before_action :set_tabs, except: %i[index destroy]
10
- before_action :set_volume, only: %i[edit destroy]
10
+ before_action :set_volume, only: %i[edit view_issues destroy]
11
+
12
+ admin_section :journal
11
13
 
12
14
  def index
13
15
  @volumes = Volume.sorted_asc
@@ -15,12 +17,20 @@ module Spina
15
17
 
16
18
  def edit; end
17
19
 
20
+ def view_issues
21
+ render layout: false
22
+ end
23
+
24
+ def new
25
+ create
26
+ end
27
+
18
28
  def create # rubocop:disable Metrics/AbcSize
19
29
  @volume = Volume.new
20
30
  @volume.journal_id = Journal.instance.id
21
31
  @volume.number = Volume.any? ? Volume.sorted_desc.first.number + 1 : 1
22
32
  @volume.save!
23
- redirect_to admin_journal_volumes_path, success: t('.created', number: @volume.number)
33
+ redirect_to edit_admin_journal_volume_path(@volume), success: t('.created', number: @volume.number)
24
34
  rescue ActiveRecord::RecordNotUnique
25
35
  # can only happen because of some race condition where two Volumes are created at the same time
26
36
  logger.error 'Error when creating new volume. Retrying...'
@@ -37,30 +47,15 @@ module Spina
37
47
  end
38
48
 
39
49
  def sort
40
- ActiveRecord::Base.transaction do
41
- sort_params.each do |id, new_pos|
42
- # ignore uniqueness validation for now
43
- Volume.find(id.to_i).update_attribute(:number, new_pos.to_i) # rubocop:disable Rails/SkipsModelValidations
44
- end
45
- validate_sort_order
50
+ params[:ids].each.with_index do |id, index|
51
+ Volume.where(id: id).update_all(number: index + 1) # rubocop:disable Rails/SkipsModelValidations
46
52
  end
47
- render json: { success: true, message: t('.sort_success') }
48
- rescue ActiveRecord::RecordInvalid
49
- render json: { success: false, message: t('.sort_error') }
53
+ flash.now[:info] = t('spina.pages.sorting_saved')
54
+ render_flash
50
55
  end
51
56
 
52
57
  private
53
58
 
54
- def sort_params
55
- params.require(:admin_journal_volumes).require(:list).permit!
56
- end
57
-
58
- def validate_sort_order
59
- Volume.where(journal_id: params[:journal_id]).each do |volume|
60
- raise ActiveRecord::RecordInvalid if volume.invalid?
61
- end
62
- end
63
-
64
59
  def set_breadcrumb
65
60
  add_breadcrumb Volume.model_name.human(count: :many), admin_journal_volumes_path
66
61
  end
@@ -26,6 +26,7 @@ module Spina
26
26
  has_many :articles, through: :affiliations
27
27
 
28
28
  validate :must_have_one_primary_affiliation
29
+ validates :orcid, format: { with: /\A((\d{4}-){3}\d{3}(\d|X))?\z/ }
29
30
 
30
31
  # @!attribute [r] primary_affiliation
31
32
  # @return [ActiveRecord::Relation] The author's primary affiliation.
@@ -16,9 +16,18 @@ module Spina
16
16
  # @!attribute [rw] position
17
17
  # @return [Integer] used to order the affiliations for each article
18
18
 
19
+ before_validation :set_default_position, unless: :persisted?
19
20
  validates :position, presence: true, uniqueness: { scope: :article_id }
20
21
 
21
22
  scope :sorted_within_article, -> { order(position: :asc) }
23
+
24
+ private
25
+
26
+ def set_default_position
27
+ return if article.authorships.sorted_within_article.last.nil?
28
+
29
+ self.position = article.authorships.sorted_within_article.last.position + 1
30
+ end
22
31
  end
23
32
  end
24
33
  end
@@ -1,29 +1,38 @@
1
- %li{ class: ('active' if %w[articles issues volumes].include? controller_name) }
2
- = link_to admin_journal_volumes_path do
3
- = icon 'pages'
4
- = ::Spina::Admin::Journal::Journal.instance.has_content?(:journal_abbreviation) ? ::Spina::Admin::Journal::Journal.instance.content(:journal_abbreviation) : ::Spina::Admin::Journal::Journal.instance.name
5
- = icon 'caret-right'
1
+ = render Spina::MainNavigation::SubNavComponent.new(:journal) do |nav|
2
+ - nav.icon do
3
+ = heroicon 'document-duplicate', style: :solid, class: 'w-8 h-8 text-white md:mr-3'
4
+ .text-white.font-semibold.hidden{class: 'md:block transform -translate-x-2 ease-in-out duration-300 absolute md:relative opacity-0 transition-all', 'data-navigation-target': 'label'}
5
+ = Spina::Admin::Journal::Journal.instance.has_content?(:journal_abbreviation) ? Spina::Admin::Journal::Journal.instance.content(:journal_abbreviation) : Spina::Admin::Journal::Journal.instance.name
6
6
 
7
- %ul
8
- %li{ class: ('active' if %w[volumes].include? controller_name) }
9
- = link_to ::Spina::Admin::Journal::Volume.model_name.human(count: :many), admin_journal_volumes_path
10
- %li{ class: ('active' if %w[issues].include? controller_name) }
11
- = link_to ::Spina::Admin::Journal::Issue.model_name.human(count: :many), admin_journal_issues_path
12
- %li{ class: ('active' if %w[articles].include? controller_name) }
13
- = link_to ::Spina::Admin::Journal::Article.model_name.human(count: :many), admin_journal_articles_path
7
+ - nav.links do
8
+ = render Spina::MainNavigation::LinkComponent.new(Spina::Admin::Journal::Volume.model_name.human(count: :many),
9
+ spina.admin_journal_volumes_path,
10
+ active: controller_name == 'volumes')
11
+ = render Spina::MainNavigation::LinkComponent.new(Spina::Admin::Journal::Issue.model_name.human(count: :many),
12
+ spina.admin_journal_issues_path,
13
+ active: controller_name == 'issues')
14
+ = render Spina::MainNavigation::LinkComponent.new(Spina::Admin::Journal::Article.model_name.human(count: :many),
15
+ spina.admin_journal_articles_path,
16
+ active: controller_name == 'articles')
14
17
 
15
- %li{ class: ('active' if %w[journals authors institutions licences].include? controller_name) }
16
- = link_to edit_admin_journal_journal_path(::Spina::Admin::Journal::Journal.instance.id) do
17
- = icon 'dots'
18
- = t 'spina.admin.journal.navigation_title'
19
- = icon 'caret-right'
20
18
 
21
- %ul
22
- %li{ class: ('active' if %w[journals].include? controller_name) }
23
- = link_to ::Spina::Admin::Journal::Journal.model_name.human(count: :one), edit_admin_journal_journal_path(::Spina::Admin::Journal::Journal.instance.id)
24
- %li{ class: ('active' if %w[authors].include? controller_name) }
25
- = link_to ::Spina::Admin::Journal::Author.model_name.human(count: :many), admin_journal_authors_path
26
- %li{ class: ('active' if %w[institutions].include? controller_name) }
27
- = link_to ::Spina::Admin::Journal::Institution.model_name.human(count: :many), admin_journal_institutions_path
28
- %li{ class: ('active' if %w[licences].include? controller_name) }
29
- = link_to ::Spina::Admin::Journal::Licence.model_name.human(count: :many), admin_journal_licences_path
19
+ = render Spina::MainNavigation::SubNavComponent.new(:journal_settings) do |nav|
20
+ - nav.icon do
21
+ = heroicon 'view-grid', style: :solid, class: 'w-8 h-8 text-white md:mr-3'
22
+ .text-white.font-semibold.hidden{class: 'md:block transform -translate-x-2 ease-in-out duration-300 absolute md:relative opacity-0 transition-all', 'data-navigation-target': 'label'}
23
+ = t 'spina.admin.journal.navigation_title'
24
+
25
+ - nav.links do
26
+ = render Spina::MainNavigation::LinkComponent.new(Spina::Admin::Journal::Journal.model_name.human(count: :one),
27
+ spina.edit_admin_journal_journal_path(Spina::Admin::Journal::Journal.instance.id),
28
+ active: controller_name == 'journals')
29
+ = render Spina::MainNavigation::LinkComponent.new(Spina::Admin::Journal::Author.model_name.human(count: :many),
30
+ spina.admin_journal_authors_path,
31
+ active: controller_name == 'authors')
32
+ = render Spina::MainNavigation::LinkComponent.new(Spina::Admin::Journal::Institution.model_name.human(count: :many),
33
+ spina.admin_journal_institutions_path,
34
+ active: controller_name == 'institutions')
35
+ = render Spina::MainNavigation::LinkComponent.new(Spina::Admin::Journal::Licence.model_name.human(count: :many),
36
+ spina.admin_journal_licences_path,
37
+ active: controller_name == 'licences')
38
+
@@ -1,29 +1,29 @@
1
- - if @article.errors.any?
2
- - content_for :notifications do
3
- .notification.notification-danger.animated.fadeInRight
4
- = icon 'exclamation'
5
- .notification-message
6
- = t 'spina.notifications.alert'
7
- %small= @article.errors.full_messages.join('<br />').html_safe
8
- = link_to '#', data: { close_notification: true } do
9
- = icon 'cross'
1
+ %div{ data: { controller: 'tabs',
2
+ tabs: { active: 'cursor-default text-gray-900 bg-spina-dark bg-opacity-10',
3
+ inactive: 'cursor-pointer bg-transparent text-gray-400 border-transparent' } } }
4
+ = render Spina::UserInterface::HeaderComponent.new do |header|
5
+ - header.actions do
6
+ - if @article.persisted?
7
+ = render Spina::UserInterface::DropdownComponent.new do |dropdown|
8
+ - dropdown.button(classes: "btn btn-default px-3") do
9
+ = heroicon('dots-horizontal', style: :solid, class: "w-5 h-5 text-gray-600")
10
10
 
11
- = form_for [:admin_journal, @article], html: { autocomplete: "off" } do |f|
12
- %header#header
13
- = render partial: 'spina/admin/shared/breadcrumbs'
11
+ - dropdown.menu do
12
+ = button_to t('spina.permanently_delete'), admin_journal_article_path(@article), method: :delete, class: "block w-full text-left px-4 py-2 text-sm leading-5 font-medium text-red-500 cursor-pointer bg-white hover:bg-red-100 hover:bg-opacity-50 hover:text-red-500 focus:outline-none focus:bg-gray-100 focus:text-gray-900", form: {data: { controller: 'confirm', confirm_message: t('.delete_confirmation', title: @article.title) } }
14
13
 
15
- #header_actions
16
- %button.button.button-primary{ type: 'submit', style: 'margin-right: 0', data: { disable_with: t('spina.pages.saving') } }
17
- = icon 'check'
14
+ = button_tag type: :submit, form: dom_id(@article), class: 'btn btn-primary', data: { controller: 'button', action: 'button#loading', loading_message: t('spina.ui.saving')} do
15
+ = heroicon('check', style: :solid, class: 'w-5 h-5 mr-1 -ml-2')
18
16
  = t '.save'
19
17
 
20
- = link_to t('spina.cancel'), admin_journal_articles_path, class: 'button', style: 'margin-right: 0;'
18
+ - header.navigation do
19
+ %nav.-mb-3.mt-4
20
+ %ul.inline-flex.w-auto.rounded-md.bg-white
21
+ - @tabs.each do |tab|
22
+ %button.block.px-3.leading-relaxed.py-1.hover:text-gray-800.rounded-md.text-gray-400.font-medium.text-sm.flex.items-center.whitespace-nowrap{ type: 'button', data: { action: 'tabs#show', 'tabs-target': 'button', 'pane-id': "#{tab}" } }
23
+ = t ".#{tab}"
21
24
 
22
- %nav#secondary.tabs
23
- %ul
24
- - @tabs.each_with_index do |tab, i|
25
- %li{ class: ('active' if i == 0) }
26
- = link_to t(".#{tab}"), "##{tab}"
25
+ .p-4.md:p-8
26
+ - @tabs.each_with_index do |tab, i|
27
+ %div{ 'data-tabs-target': 'pane', id: tab, hidden: i != 0 }
28
+ .max-w-5xl= render "form_#{tab}"
27
29
 
28
- #details.tab-content.active= render 'form_details', f: f
29
- #authors.tab-content= render 'form_authors', f: f
@@ -1,15 +1,3 @@
1
- .sort-message
2
- .table-container
3
- %table.table
4
- %thead
5
- %tr
6
- %th= ::Spina::Admin::Journal::Affiliation.human_attribute_name :position
7
- %th= ::Spina::Admin::Journal::Affiliation.human_attribute_name :name
8
- %th= ::Spina::Admin::Journal::Affiliation.human_attribute_name :institution
9
- %th= t '.number_of_articles'
10
- %th
11
- %tbody.html5sortable{ data: { id: @article.id, sorted_collection: 'admin_journal_authorships', sort_url: !@article.id.nil? && sort_admin_journal_authors_url(@article) } }
12
- - if @article.authorships.any?
13
- = render @article.authorships.sorted_within_article
14
- - else
15
- = render 'empty_list', message: t('.no_authors')
1
+ - if @article.persisted?
2
+ .-mt-4.md:-mt-8
3
+ %turbo-frame#article_authors{ src: spina.view_authors_admin_journal_article_path(@article), loading: 'lazy' }