lcms-engine 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.codeclimate.yml +1 -1
- data/.env.docker +1 -1
- data/.rubocop.yml +8 -7
- data/.ruby-version +1 -1
- data/CHANGELOG.md +16 -1
- data/Dockerfile +1 -1
- data/Gemfile.lock +223 -184
- data/README.md +3 -2
- data/app/controllers/concerns/lcms/engine/reimportable.rb +11 -1
- data/app/controllers/lcms/engine/admin/admin_controller.rb +1 -1
- data/app/controllers/lcms/engine/admin/documents_controller.rb +19 -4
- data/app/controllers/lcms/engine/admin/materials_controller.rb +12 -3
- data/app/controllers/lcms/engine/admin/standards_controller.rb +6 -1
- data/app/controllers/lcms/engine/admin/users_controller.rb +6 -1
- data/app/entities/lcms/engine/grades.rb +17 -6
- data/app/entities/lcms/engine/media_embed.rb +1 -1
- data/app/entities/lcms/engine/roman_numerals.rb +1 -1
- data/app/forms/lcms/engine/document_form.rb +19 -14
- data/app/forms/lcms/engine/material_form.rb +3 -2
- data/app/helpers/admin/components_helper.rb +2 -2
- data/app/helpers/lcms/engine/view_helper.rb +1 -1
- data/app/interactors/lcms/engine/explore_curriculum_interactor.rb +1 -1
- data/app/javascript/components/admin/ImportStatus.jsx +7 -3
- data/app/javascript/components/admin/MultiSelectedOperation.jsx +1 -0
- data/app/javascript/components/admin/association-picker/AssociationPicker.jsx +1 -0
- data/app/javascript/components/admin/association-picker/AssociationPickerResults.jsx +1 -0
- data/app/javascript/components/admin/association-picker/AssociationPickerWindow.jsx +1 -0
- data/app/javascript/components/admin/curriculum/CurriculumEditor.jsx +1 -0
- data/app/javascript/components/admin/resource-picker/ResourcePicker.jsx +1 -0
- data/app/jobs/concerns/lcms/engine/nested_resque_job.rb +1 -4
- data/app/jobs/lcms/engine/document_generate_gdoc_job.rb +2 -2
- data/app/jobs/lcms/engine/document_generate_job.rb +1 -1
- data/app/jobs/lcms/engine/document_parse_job.rb +1 -1
- data/app/models/lcms/engine/component.rb +3 -3
- data/app/models/lcms/engine/download.rb +1 -1
- data/app/models/lcms/engine/search/document.rb +4 -3
- data/app/presenters/lcms/engine/document_presenter.rb +2 -2
- data/app/queries/lcms/engine/admin_documents_query.rb +1 -1
- data/app/serializers/lcms/engine/previews_material_serializer.rb +1 -0
- data/app/services/lcms/engine/document_build_service.rb +4 -0
- data/app/services/lcms/engine/html_sanitizer.rb +5 -5
- data/app/services/lcms/engine/lessons_gdoc_bundler.rb +1 -1
- data/app/services/lcms/engine/material_build_service.rb +6 -2
- data/app/services/lcms/engine/material_preview_generator.rb +2 -2
- data/app/services/lcms/engine/react_materials_resolver.rb +3 -2
- data/app/views/lcms/engine/admin/batch_reimports/_search_form.html.erb +1 -1
- data/app/views/lcms/engine/admin/curriculums/edit.html.erb +1 -3
- data/app/views/lcms/engine/admin/documents/_materials_links.html.erb +2 -2
- data/app/views/lcms/engine/admin/documents/_search_form.html.erb +1 -1
- data/app/views/lcms/engine/admin/documents/index.html.erb +2 -2
- data/app/views/lcms/engine/admin/documents/new.html.erb +1 -1
- data/app/views/lcms/engine/admin/materials/_search_form.html.erb +1 -1
- data/app/views/lcms/engine/admin/materials/index.html.erb +4 -4
- data/app/views/lcms/engine/admin/resource_bulk_edits/new.html.erb +1 -1
- data/app/views/lcms/engine/admin/resources/_fields.html.erb +3 -0
- data/app/views/lcms/engine/admin/resources/_search_form.html.erb +1 -1
- data/app/views/lcms/engine/documents/gdoc/_agenda.html.erb +1 -1
- data/app/views/lcms/engine/documents/show.html.erb +1 -1
- data/app/views/lcms/engine/materials/show.html.erb +1 -1
- data/app/views/lcms/engine/resources/_download.html.erb +2 -2
- data/app/views/lcms/engine/resources/_unit_bundles.html.erb +2 -2
- data/config/locales/admin/en.yml +1 -1
- data/db/schema.rb +1 -1
- data/lcms-engine.gemspec +12 -10
- data/lib/doc_template.rb +1 -1
- data/lib/doc_template/objects/agenda_metadata.rb +1 -1
- data/lib/doc_template/objects/metadata_helpers.rb +1 -1
- data/lib/doc_template/objects/toc_metadata.rb +2 -2
- data/lib/doc_template/tables/base.rb +3 -2
- data/lib/doc_template/tags/activity_metadata_type_tag.rb +1 -1
- data/lib/doc_template/tags/answer_space_tag.rb +1 -1
- data/lib/doc_template/tags/base_tag.rb +3 -3
- data/lib/doc_template/tags/columns_tag.rb +1 -1
- data/lib/doc_template/tags/def_tag.rb +1 -1
- data/lib/doc_template/tags/expand_tag.rb +1 -0
- data/lib/doc_template/tags/heading_tag.rb +1 -1
- data/lib/doc_template/tags/inset_tag.rb +2 -2
- data/lib/doc_template/tags/latex_tag.rb +1 -1
- data/lib/doc_template/tags/page_break_tag.rb +1 -1
- data/lib/doc_template/tags/pd_tag.rb +4 -4
- data/lib/doc_template/tags/section_tag.rb +2 -2
- data/lib/doc_template/tags/standard_tag.rb +3 -3
- data/lib/doc_template/tags/table_preserve_alignment_tag.rb +1 -1
- data/lib/document_exporter/gdoc/base.rb +1 -1
- data/lib/document_renderer/part.rb +3 -3
- data/lib/elasticsearch/persistence/repository/response/results.rb +1 -1
- data/lib/lcms/engine/engine.rb +1 -1
- data/lib/lcms/engine/version.rb +1 -1
- data/lib/lt/lcms/metadata/base_service.rb +2 -1
- data/lib/lt/lcms/metadata/context.rb +2 -2
- data/lib/lt/lcms/metadata/service.rb +3 -1
- data/lib/resque_job.rb +3 -6
- data/lib/standard_importer.rb +4 -4
- data/lib/tasks/cloud66.rake +6 -4
- data/lib/tasks/document.rake +3 -3
- data/lib/tasks/google.rake +1 -1
- data/package.json +1 -0
- data/spec/controllers/admin/association_picker_controller_spec.rb +6 -8
- data/spec/controllers/admin/documents_controller_spec.rb +3 -1
- data/spec/controllers/admin/materials_controller_spec.rb +1 -1
- data/spec/controllers/admin/resources_controller_spec.rb +2 -2
- data/spec/dummy/.env.docker +5 -0
- data/spec/dummy/config/environments/production.rb +1 -1
- data/spec/dummy/config/puma.rb +3 -3
- data/spec/entities/grades_spec.rb +12 -0
- data/spec/features/admin/lessons/add_lesson_spec.rb +3 -3
- data/spec/features/admin/materials/add_material_spec.rb +3 -3
- data/spec/forms/document_form_spec.rb +11 -1
- data/spec/lib/doc_template/tables/shared_examples/remove_table.rb +1 -1
- data/spec/rails_helper.rb +19 -2
- data/spec/services/document_build_service_spec.rb +1 -1
- data/yarn.lock +145 -170
- metadata +81 -34
data/README.md
CHANGED
@@ -13,8 +13,9 @@ separately, simplifying the client applications in the process.
|
|
13
13
|
|
14
14
|
|Branch|Rails version|
|
15
15
|
|------|-------------|
|
16
|
-
|master|Rails
|
17
|
-
|0.
|
16
|
+
|master|Rails 6|
|
17
|
+
|0.3.x|Rails 5.2.4.5|
|
18
|
+
|0.1.x|Rails 4.2.11.3|
|
18
19
|
|
19
20
|
This is still a [work in progress](https://github.com/learningtapestry/lcms-engine/issues/3). The
|
20
21
|
initial phase of the project consisted in extracting as much code as possible from the client
|
@@ -19,10 +19,20 @@ module Lcms
|
|
19
19
|
status = job_class.status(jid)
|
20
20
|
obj[jid] = {
|
21
21
|
status: status,
|
22
|
-
result: (status == :done ? job_class
|
22
|
+
result: (status == :done ? prepare_result(job_class, jid) : nil)
|
23
23
|
}.compact
|
24
24
|
end
|
25
25
|
end
|
26
|
+
|
27
|
+
def prepare_result(job_class, jid)
|
28
|
+
jid_res = job_class.fetch_result(jid)
|
29
|
+
return jid_res if jid_res['ok']
|
30
|
+
|
31
|
+
{
|
32
|
+
ok: false,
|
33
|
+
errors: Array.wrap("<a href=\"#{jid_res['link']}\">Source</a>: #{jid_res['errors'].join(', ')}")
|
34
|
+
}
|
35
|
+
end
|
26
36
|
end
|
27
37
|
end
|
28
38
|
end
|
@@ -10,9 +10,10 @@ module Lcms
|
|
10
10
|
include Reimportable
|
11
11
|
|
12
12
|
before_action :find_selected, only: %i(destroy_selected reimport_selected)
|
13
|
+
before_action :set_query_params
|
13
14
|
|
14
15
|
def index
|
15
|
-
@query = OpenStruct.new
|
16
|
+
@query = OpenStruct.new @query_params
|
16
17
|
@documents = DocTemplate.config['queries']['document'].constantize.call(@query, page: params[:page])
|
17
18
|
render_customized_view
|
18
19
|
end
|
@@ -23,7 +24,7 @@ module Lcms
|
|
23
24
|
@document = reimport_lesson
|
24
25
|
if @document.save
|
25
26
|
redirect_to AdminController.document_path(@document.document),
|
26
|
-
notice: t('.success', name: @document.document.name)
|
27
|
+
notice: t('.success', name: @document.document.name, errors: collect_errors)
|
27
28
|
else
|
28
29
|
render :new
|
29
30
|
end
|
@@ -32,12 +33,12 @@ module Lcms
|
|
32
33
|
def destroy
|
33
34
|
@document = Document.find(params[:id])
|
34
35
|
@document.destroy
|
35
|
-
redirect_to admin_documents_path(query:
|
36
|
+
redirect_to admin_documents_path(query: @query_params), notice: t('.success')
|
36
37
|
end
|
37
38
|
|
38
39
|
def destroy_selected
|
39
40
|
count = @documents.destroy_all.count
|
40
|
-
redirect_to admin_documents_path(query:
|
41
|
+
redirect_to admin_documents_path(query: @query_params), notice: t('.success', count: count)
|
41
42
|
end
|
42
43
|
|
43
44
|
def import_status
|
@@ -67,6 +68,12 @@ module Lcms
|
|
67
68
|
@props = { jobs: jobs, type: :documents, links: view_links }
|
68
69
|
end
|
69
70
|
|
71
|
+
def collect_errors
|
72
|
+
return if @document.service_errors.empty?
|
73
|
+
|
74
|
+
"Errors: #{@document.service_errors.join(' ')}"
|
75
|
+
end
|
76
|
+
|
70
77
|
def find_selected
|
71
78
|
return head(:bad_request) unless params[:selected_ids].present?
|
72
79
|
|
@@ -105,6 +112,14 @@ module Lcms
|
|
105
112
|
MaterialForm.new({ link: material.file_url, source_type: material.source_type }, google_credentials).save
|
106
113
|
end
|
107
114
|
end
|
115
|
+
|
116
|
+
def set_query_params
|
117
|
+
@query_params = params[:query]
|
118
|
+
&.permit(
|
119
|
+
:broken_materials, :course, :grade, :inactive, :locale, :module, :only_failed, :reimport_required,
|
120
|
+
:search_term, :sort_by
|
121
|
+
) || {}
|
122
|
+
end
|
108
123
|
end
|
109
124
|
end
|
110
125
|
end
|
@@ -8,9 +8,10 @@ module Lcms
|
|
8
8
|
include Reimportable
|
9
9
|
|
10
10
|
before_action :find_selected, only: %i(destroy_selected reimport_selected)
|
11
|
+
before_action :set_query_params
|
11
12
|
|
12
13
|
def index
|
13
|
-
@query = OpenStruct.new
|
14
|
+
@query = OpenStruct.new @query_params
|
14
15
|
@materials = DocTemplate.config['queries']['material'].constantize.call(@query, page: params[:page])
|
15
16
|
render_customized_view
|
16
17
|
end
|
@@ -32,12 +33,12 @@ module Lcms
|
|
32
33
|
def destroy
|
33
34
|
material = Material.find(params[:id])
|
34
35
|
material.destroy
|
35
|
-
redirect_to admin_materials_path(query:
|
36
|
+
redirect_to admin_materials_path(query: @query_params), notice: t('.success')
|
36
37
|
end
|
37
38
|
|
38
39
|
def destroy_selected
|
39
40
|
count = @materials.destroy_all.count
|
40
|
-
redirect_to admin_materials_path(query:
|
41
|
+
redirect_to admin_materials_path(query: @query_params), notice: t('.success', count: count)
|
41
42
|
end
|
42
43
|
|
43
44
|
def import_status
|
@@ -89,6 +90,14 @@ module Lcms
|
|
89
90
|
.map { |id| ::Lt::Lcms::Lesson::Downloader::Gdoc.gdoc_file_url(id) }
|
90
91
|
end
|
91
92
|
end
|
93
|
+
|
94
|
+
def set_query_params
|
95
|
+
@query_params = params[:query]
|
96
|
+
&.permit(
|
97
|
+
:course, :lesson, :name_date, :orientation, :search_term, :search_file_name, :sort_by, :title, :type,
|
98
|
+
:unit
|
99
|
+
) || {}
|
100
|
+
end
|
92
101
|
end
|
93
102
|
end
|
94
103
|
end
|
@@ -5,11 +5,12 @@ module Lcms
|
|
5
5
|
module Admin
|
6
6
|
class StandardsController < AdminController
|
7
7
|
before_action :find_standard, except: [:index]
|
8
|
+
before_action :set_query_params
|
8
9
|
|
9
10
|
def edit; end
|
10
11
|
|
11
12
|
def index
|
12
|
-
@query = OpenStruct.new
|
13
|
+
@query = OpenStruct.new @query_params
|
13
14
|
|
14
15
|
scope = Standard.order(:id)
|
15
16
|
scope = scope.search_by_name(@query.name) if @query.name.present?
|
@@ -31,6 +32,10 @@ module Lcms
|
|
31
32
|
@standard = Standard.find(params[:id])
|
32
33
|
end
|
33
34
|
|
35
|
+
def set_query_params
|
36
|
+
@query_params = params[:query]&.permit(:name) || {}
|
37
|
+
end
|
38
|
+
|
34
39
|
def standard_params
|
35
40
|
params.require(:standard).permit(:description)
|
36
41
|
end
|
@@ -5,9 +5,10 @@ module Lcms
|
|
5
5
|
module Admin
|
6
6
|
class UsersController < AdminController
|
7
7
|
before_action :find_user, except: %i(index new create)
|
8
|
+
before_action :set_query_params
|
8
9
|
|
9
10
|
def index
|
10
|
-
@query = OpenStruct.new
|
11
|
+
@query = OpenStruct.new @query_params
|
11
12
|
@users = users(@query)
|
12
13
|
end
|
13
14
|
|
@@ -52,6 +53,10 @@ module Lcms
|
|
52
53
|
@user = User.find(params[:id])
|
53
54
|
end
|
54
55
|
|
56
|
+
def set_query_params
|
57
|
+
@query_params = params[:query]&.permit(:access_code, :email) || {}
|
58
|
+
end
|
59
|
+
|
55
60
|
def user_params
|
56
61
|
params.require(:user).permit(:access_code, :email, :name, :role)
|
57
62
|
end
|
@@ -12,31 +12,42 @@ module Lcms
|
|
12
12
|
|
13
13
|
attr_reader :model
|
14
14
|
|
15
|
+
class << self
|
16
|
+
def grades
|
17
|
+
::Lcms::Engine::Grades::GRADES
|
18
|
+
end
|
19
|
+
|
20
|
+
def grades_abbrevs
|
21
|
+
::Lcms::Engine::Grades::GRADES_ABBR
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
15
25
|
def initialize(model)
|
16
26
|
@model = model
|
17
27
|
end
|
18
28
|
|
19
29
|
def list
|
20
|
-
@list ||=
|
30
|
+
@list ||= case model
|
31
|
+
when Resource
|
21
32
|
Array.wrap model.metadata['grade']
|
22
|
-
|
33
|
+
when Search::Document
|
23
34
|
Array.wrap model.grade.presence
|
24
35
|
else
|
25
36
|
model.grade_list
|
26
|
-
end.sort_by { |g|
|
37
|
+
end.sort_by { |g| self.class.grades.index(g) }
|
27
38
|
end
|
28
39
|
|
29
40
|
def average(abbr: true)
|
30
41
|
return nil if average_number.nil?
|
31
42
|
|
32
|
-
avg =
|
43
|
+
avg = self.class.grades[average_number]
|
33
44
|
abbr ? (grade_abbr(avg) || 'base') : avg
|
34
45
|
end
|
35
46
|
|
36
47
|
def average_number
|
37
48
|
return nil if list.empty?
|
38
49
|
|
39
|
-
list.map { |g|
|
50
|
+
list.map { |g| self.class.grades.index(g) }.sum / (list.size.nonzero? || 1)
|
40
51
|
end
|
41
52
|
|
42
53
|
def grade_abbr(abbr)
|
@@ -62,7 +73,7 @@ module Lcms
|
|
62
73
|
abbr = grade_abbr(g).upcase
|
63
74
|
|
64
75
|
# if the current grade is subsequent we store on the same chain
|
65
|
-
if idx.zero? ||
|
76
|
+
if idx.zero? || self.class.grades.index(g) == self.class.grades.index(prev) + 1
|
66
77
|
chain << abbr
|
67
78
|
else
|
68
79
|
# the grade is not subsequent, so we store the current chain, and create a new one
|
@@ -26,7 +26,7 @@ module Lcms
|
|
26
26
|
when /vimeo\.com/
|
27
27
|
url.match(%r{https?://(www\.)?vimeo.com/(\d+)}).try(:[], 2)
|
28
28
|
when /youtu\.be/
|
29
|
-
url.match(%r{https?://(www\.)?youtu\.be/([^"
|
29
|
+
url.match(%r{https?://(www\.)?youtu\.be/([^"&?/]+)}).try(:[], 2)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
@@ -13,7 +13,7 @@ module Lcms
|
|
13
13
|
validates_presence_of :link, if: -> { link_fs.blank? }
|
14
14
|
validates_presence_of :link_fs, if: -> { link.blank? }
|
15
15
|
|
16
|
-
attr_reader :document
|
16
|
+
attr_reader :document, :service_errors
|
17
17
|
|
18
18
|
def initialize(attributes = {}, opts = {})
|
19
19
|
@is_reimport = attributes.delete(:reimport).present? || false
|
@@ -29,7 +29,7 @@ module Lcms
|
|
29
29
|
@document.update(reimported: true)
|
30
30
|
rescue StandardError => e
|
31
31
|
@document&.update(reimported: false)
|
32
|
-
Rails.logger.error e.message
|
32
|
+
Rails.logger.error "#{e.message}\n #{e.backtrace.join("\n ")}"
|
33
33
|
errors.add(:link, e.message)
|
34
34
|
false
|
35
35
|
end
|
@@ -45,18 +45,23 @@ module Lcms
|
|
45
45
|
def build_document
|
46
46
|
service = document_build_service
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
48
|
+
result =
|
49
|
+
if is_reimport
|
50
|
+
doc = service.build_for(link)
|
51
|
+
doc = service.build_for(link_fs, expand: true) if link_fs.present?
|
52
|
+
doc
|
53
|
+
elsif (full_doc = find_full_document)
|
54
|
+
# if there is a document with the same file_id or foundational_file_id
|
55
|
+
# we need to make full re-import to correctly handle expand process
|
56
|
+
service.build_for(full_doc.file_url)
|
57
|
+
service.build_for(full_doc.file_fs_url, expand: true)
|
58
|
+
else
|
59
|
+
service.build_for link
|
60
|
+
end
|
61
|
+
|
62
|
+
@service_errors = service.errors
|
63
|
+
|
64
|
+
result
|
60
65
|
end
|
61
66
|
|
62
67
|
def document_build_service
|
@@ -11,7 +11,7 @@ module Lcms
|
|
11
11
|
attribute :source_type, String
|
12
12
|
validates :link, presence: true
|
13
13
|
|
14
|
-
attr_accessor :material
|
14
|
+
attr_accessor :material, :service_errors
|
15
15
|
|
16
16
|
def initialize(attributes = {}, opts = {})
|
17
17
|
super(attributes)
|
@@ -28,12 +28,13 @@ module Lcms
|
|
28
28
|
}.compact
|
29
29
|
service = MaterialBuildService.new google_credentials, params
|
30
30
|
@material = service.build link
|
31
|
+
@service_errors = service.errors
|
31
32
|
|
32
33
|
material.update preview_links: {}
|
33
34
|
after_reimport_hook
|
34
35
|
true
|
35
36
|
rescue StandardError => e
|
36
|
-
Rails.logger.error e.message
|
37
|
+
Rails.logger.error "#{e.message}\n #{e.backtrace.join("\n ")}"
|
37
38
|
errors.add(:link, e.message)
|
38
39
|
false
|
39
40
|
end
|
@@ -2,9 +2,9 @@
|
|
2
2
|
|
3
3
|
module Admin
|
4
4
|
module ComponentsHelper
|
5
|
-
HIDDEN_FIELD_RE = /\b(?<=name=")[^"]+(?=")
|
5
|
+
HIDDEN_FIELD_RE = /\b(?<=name=")[^"]+(?=")/.freeze
|
6
6
|
|
7
|
-
def resource_picker_field(form, collection,
|
7
|
+
def resource_picker_field(form, collection, path:, name:, allow_multiple: true)
|
8
8
|
path = path.to_s
|
9
9
|
|
10
10
|
base_name = form.hidden_field(name).scan(HIDDEN_FIELD_RE)[0]
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import React from 'react'
|
2
|
+
import _ from 'lodash'
|
2
3
|
|
3
4
|
class ImportStatus extends React.Component {
|
4
5
|
constructor(props) {
|
@@ -53,7 +54,8 @@ class ImportStatus extends React.Component {
|
|
53
54
|
if (this.withPdf) {
|
54
55
|
return (
|
55
56
|
<a href={job.link}
|
56
|
-
className="o-adm-materials__resource
|
57
|
+
className="o-adm-materials__resource button primary u-margin-left--small u-margin-bottom--zero" target="_blank">
|
58
|
+
<i className="far fa-file-pdf"></i>
|
57
59
|
</a>
|
58
60
|
)
|
59
61
|
}
|
@@ -69,7 +71,8 @@ class ImportStatus extends React.Component {
|
|
69
71
|
return _.map(this.links, (link, idx) => (
|
70
72
|
<a key={`pl-${idx}`}
|
71
73
|
href={linkWithParams(link, { id: job.model.id })}
|
72
|
-
className="o-adm-materials__resource
|
74
|
+
className="o-adm-materials__resource button primary u-margin-left--small u-margin-bottom--zero" target="_blank">
|
75
|
+
<i className="fas fa-eye"></i>
|
73
76
|
</a>
|
74
77
|
))
|
75
78
|
}
|
@@ -99,7 +102,8 @@ class ImportStatus extends React.Component {
|
|
99
102
|
{job.status !== 'done' ? this.spinner() : null}
|
100
103
|
{job.status === 'done' && job.ok ? <span>{this.resourceButton(job)}</span> : null}
|
101
104
|
</div>
|
102
|
-
{job.errors ? (<p dangerouslySetInnerHTML={{__html: _.join(job.errors, '<br/>')}}></p>) : null}
|
105
|
+
{!(_.isEmpty(job.errors)) ? (<p dangerouslySetInnerHTML={{__html: _.join(job.errors, '<br/>')}}></p>) : null}
|
106
|
+
{!(_.isEmpty(job.warnings)) ? (<p dangerouslySetInnerHTML={{ __html: _.join(job.warnings, '<br/>') }}></p>) : null}
|
103
107
|
</li>
|
104
108
|
)
|
105
109
|
})
|