Merge pull request #3185 from biosistemika/develop

Version 1.21.2
This commit is contained in:
Miha Mencin 2021-02-23 17:54:00 +01:00 committed by GitHub
commit 71ccf6ec27
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
180 changed files with 6525 additions and 3824 deletions

View file

@ -1,4 +1,4 @@
FROM ruby:2.6.4-buster
FROM ruby:2.7.2-buster
MAINTAINER BioSistemika <info@biosistemika.com>
ARG WKHTMLTOPDF_PACKAGE_URL=https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.5/wkhtmltox_0.12.5-1.stretch_amd64.deb

View file

@ -1,4 +1,4 @@
FROM ruby:2.6.4-buster
FROM ruby:2.7.2-buster
MAINTAINER BioSistemika <info@biosistemika.com>
ARG WKHTMLTOPDF_PACKAGE_URL=https://github.com/wkhtmltopdf/wkhtmltopdf/releases/download/0.12.5/wkhtmltox_0.12.5-1.stretch_amd64.deb

View file

@ -2,7 +2,7 @@
source 'http://rubygems.org'
ruby '2.6.4'
ruby '2.7.2'
gem 'bootsnap', require: false
gem 'bootstrap-sass', '~> 3.4.1'
@ -12,7 +12,7 @@ gem 'devise_invitable'
gem 'figaro'
gem 'pg', '~> 1.1'
gem 'pg_search' # PostgreSQL full text search
gem 'rails', '~> 6.0.0'
gem 'rails', '~> 6.1.1'
gem 'recaptcha', require: 'recaptcha/rails'
gem 'sanitize', '~> 5.2'
gem 'sassc-rails'
@ -66,7 +66,7 @@ gem 'deface', '~> 1.0'
gem 'down', '~> 5.0'
gem 'faker' # Generate fake data
gem 'fastimage' # Light gem to get image resolution
gem 'httparty', '~> 0.13.1'
gem 'httparty', '~> 0.17.3'
gem 'i18n-js', '~> 3.6' # Localization in javascript files
gem 'jbuilder' # JSON structures via a Builder-style DSL
gem 'logging', '~> 2.0.0'

View file

@ -17,7 +17,7 @@ GIT
GIT
remote: https://github.com/biosistemika/yomu
revision: 063b855d672e9dd9de1e6e585b349a9b63e120c3
revision: 3baa161e76b433ede55142dd1bb7cc04159f93f0
branch: master
specs:
yomu (0.2.4)
@ -42,115 +42,134 @@ GIT
GEM
remote: http://rubygems.org/
specs:
actioncable (6.0.3)
actionpack (= 6.0.3)
actioncable (6.1.1)
actionpack (= 6.1.1)
activesupport (= 6.1.1)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (6.0.3)
actionpack (= 6.0.3)
activejob (= 6.0.3)
activerecord (= 6.0.3)
activestorage (= 6.0.3)
activesupport (= 6.0.3)
actionmailbox (6.1.1)
actionpack (= 6.1.1)
activejob (= 6.1.1)
activerecord (= 6.1.1)
activestorage (= 6.1.1)
activesupport (= 6.1.1)
mail (>= 2.7.1)
actionmailer (6.0.3)
actionpack (= 6.0.3)
actionview (= 6.0.3)
activejob (= 6.0.3)
actionmailer (6.1.1)
actionpack (= 6.1.1)
actionview (= 6.1.1)
activejob (= 6.1.1)
activesupport (= 6.1.1)
mail (~> 2.5, >= 2.5.4)
rails-dom-testing (~> 2.0)
actionpack (6.0.3)
actionview (= 6.0.3)
activesupport (= 6.0.3)
rack (~> 2.0, >= 2.0.8)
actionpack (6.1.1)
actionview (= 6.1.1)
activesupport (= 6.1.1)
rack (~> 2.0, >= 2.0.9)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (6.0.3)
actionpack (= 6.0.3)
activerecord (= 6.0.3)
activestorage (= 6.0.3)
activesupport (= 6.0.3)
actiontext (6.1.1)
actionpack (= 6.1.1)
activerecord (= 6.1.1)
activestorage (= 6.1.1)
activesupport (= 6.1.1)
nokogiri (>= 1.8.5)
actionview (6.0.3)
activesupport (= 6.0.3)
actionview (6.1.1)
activesupport (= 6.1.1)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
active_model_serializers (0.10.10)
actionpack (>= 4.1, < 6.1)
activemodel (>= 4.1, < 6.1)
active_model_serializers (0.10.12)
actionpack (>= 4.1, < 6.2)
activemodel (>= 4.1, < 6.2)
case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
activejob (6.0.3)
activesupport (= 6.0.3)
activejob (6.1.1)
activesupport (= 6.1.1)
globalid (>= 0.3.6)
activemodel (6.0.3)
activesupport (= 6.0.3)
activerecord (6.0.3)
activemodel (= 6.0.3)
activesupport (= 6.0.3)
activerecord-import (1.0.4)
activemodel (6.1.1)
activesupport (= 6.1.1)
activerecord (6.1.1)
activemodel (= 6.1.1)
activesupport (= 6.1.1)
activerecord-import (1.0.7)
activerecord (>= 3.2)
activestorage (6.0.3)
actionpack (= 6.0.3)
activejob (= 6.0.3)
activerecord (= 6.0.3)
activestorage (6.1.1)
actionpack (= 6.1.1)
activejob (= 6.1.1)
activerecord (= 6.1.1)
activesupport (= 6.1.1)
marcel (~> 0.3.1)
activesupport (6.0.3)
mimemagic (~> 0.3.2)
activesupport (6.1.1)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
zeitwerk (~> 2.2, >= 2.2.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
zeitwerk (~> 2.3)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
aes_key_wrap (1.0.1)
aes_key_wrap (1.1.0)
ajax-datatables-rails (0.3.1)
railties (>= 3.1)
aspector (0.14.0)
ast (2.4.1)
auto_strip_attributes (2.5.0)
ast (2.4.2)
auto_strip_attributes (2.6.0)
activerecord (>= 4.0)
autoprefixer-rails (9.7.0)
autoprefixer-rails (10.2.0.0)
execjs
autosize-rails (1.18.17)
rails (>= 3.1)
awesome_print (1.8.0)
aws-eventstream (1.0.3)
aws-partitions (1.211.0)
aws-sdk-core (3.67.0)
aws-eventstream (~> 1.0, >= 1.0.2)
aws-partitions (~> 1.0)
aws-eventstream (1.1.0)
aws-partitions (1.420.0)
aws-record (2.5.0)
aws-sdk-dynamodb (~> 1.18)
aws-sdk-core (3.111.2)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.239.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.24.0)
aws-sdk-core (~> 3, >= 3.61.1)
aws-sdk-dynamodb (1.58.0)
aws-sdk-core (~> 3, >= 3.109.0)
aws-sigv4 (~> 1.1)
aws-sdk-rails (2.1.0)
aws-sdk-kms (1.41.0)
aws-sdk-core (~> 3, >= 3.109.0)
aws-sigv4 (~> 1.1)
aws-sdk-rails (3.6.0)
aws-record (~> 2)
aws-sdk-ses (~> 1)
railties (>= 3)
aws-sdk-s3 (1.48.0)
aws-sdk-core (~> 3, >= 3.61.1)
aws-sdk-sqs (~> 1)
aws-sessionstore-dynamodb (~> 2)
concurrent-ruby (~> 1)
railties (>= 5.2.0)
aws-sdk-s3 (1.87.0)
aws-sdk-core (~> 3, >= 3.109.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.1)
aws-sdk-ses (1.25.0)
aws-sdk-core (~> 3, >= 3.61.1)
aws-sdk-ses (1.36.0)
aws-sdk-core (~> 3, >= 3.109.0)
aws-sigv4 (~> 1.1)
aws-sigv4 (1.1.0)
aws-eventstream (~> 1.0, >= 1.0.2)
backports (3.15.0)
aws-sdk-sqs (1.35.0)
aws-sdk-core (~> 3, >= 3.109.0)
aws-sigv4 (~> 1.1)
aws-sessionstore-dynamodb (2.0.1)
aws-sdk-dynamodb (~> 1)
rack (~> 2)
aws-sigv4 (1.2.2)
aws-eventstream (~> 1, >= 1.0.2)
backports (3.20.2)
base62 (1.0.0)
bcrypt (3.1.13)
better_errors (2.5.1)
bcrypt (3.1.16)
better_errors (2.9.1)
coderay (>= 1.0.0)
erubi (>= 1.0.0)
rack (>= 0.9.0)
bindata (2.4.4)
binding_of_caller (0.8.0)
bindata (2.4.8)
binding_of_caller (1.0.0)
debug_inspector (>= 0.0.1)
bootsnap (1.4.5)
bootsnap (1.6.0)
msgpack (~> 1.0)
bootstrap-sass (3.4.1)
autoprefixer-rails (>= 5.2.1)
@ -160,34 +179,33 @@ GEM
momentjs-rails (>= 2.8.1)
bootstrap_form (2.7.0)
builder (3.2.4)
bullet (6.0.2)
bullet (6.1.3)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
byebug (11.0.1)
capybara (3.29.0)
byebug (11.1.3)
capybara (3.35.1)
addressable
mini_mime (>= 0.1.3)
nokogiri (~> 1.8)
rack (>= 1.6.0)
rack-test (>= 0.6.3)
regexp_parser (~> 1.5)
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
capybara-email (3.0.1)
capybara-email (3.0.2)
capybara (>= 2.4, < 4.0)
mail
caracal (1.4.1)
nokogiri (~> 1.6)
rubyzip (~> 1.1)
tilt (>= 1.4)
caracal-rails (1.0.1)
caracal-rails (1.0.2)
caracal (~> 1.0)
rails (>= 3.2)
case_transform (0.2)
activesupport
childprocess (1.0.1)
rake (< 13.0)
chunky_png (1.3.11)
coderay (1.1.2)
childprocess (3.0.0)
chunky_png (1.4.0)
coderay (1.1.3)
coffee-rails (5.0.0)
coffee-script (>= 2.2.0)
railties (>= 5.2.0)
@ -195,11 +213,11 @@ GEM
coffee-script-source
execjs
coffee-script-source (1.12.2)
concurrent-ruby (1.1.6)
crack (0.4.3)
safe_yaml (~> 1.0.0)
concurrent-ruby (1.1.8)
crack (0.4.5)
rexml
crass (1.0.6)
cucumber (3.1.2)
cucumber (3.2.0)
builder (>= 2.1.2)
cucumber-core (~> 3.2.0)
cucumber-expressions (~> 6.0.1)
@ -221,76 +239,80 @@ GEM
railties (>= 4.2, < 7)
cucumber-tag_expressions (1.1.1)
cucumber-wire (0.0.1)
database_cleaner (1.7.0)
debug_inspector (0.0.3)
deface (1.5.1)
database_cleaner (1.8.5)
debug_inspector (1.0.0)
deface (1.6.1)
nokogiri (>= 1.6)
polyglot
rails (>= 4.1)
rails (>= 5.2)
rainbow (>= 2.1.0)
delayed_job (4.1.8)
activesupport (>= 3.0, < 6.1)
delayed_job_active_record (4.1.4)
activerecord (>= 3.0, < 6.1)
delayed_job (4.1.9)
activesupport (>= 3.0, < 6.2)
delayed_job_active_record (4.1.5)
activerecord (>= 3.0, < 6.2)
delayed_job (>= 3.0, < 5)
devise (4.7.1)
devise (4.7.3)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
railties (>= 4.1.0)
responders
warden (~> 1.2.3)
devise_invitable (2.0.1)
devise_invitable (2.0.3)
actionmailer (>= 5.0)
devise (>= 4.6)
diff-lcs (1.3)
discard (1.1.0)
diff-lcs (1.4.4)
discard (1.2.0)
activerecord (>= 4.2, < 7)
docile (1.3.2)
doorkeeper (5.1.1)
docile (1.3.5)
doorkeeper (5.4.0)
railties (>= 5)
down (5.0.0)
down (5.2.0)
addressable (~> 2.5)
erubi (1.9.0)
et-orbi (1.2.2)
erubi (1.10.0)
et-orbi (1.2.4)
tzinfo
execjs (2.7.0)
factory_bot (5.0.2)
activesupport (>= 4.2.0)
factory_bot_rails (5.0.2)
factory_bot (~> 5.0.2)
railties (>= 4.2.0)
faker (2.2.2)
i18n (~> 1.6.0)
faraday (0.15.4)
factory_bot (6.1.0)
activesupport (>= 5.0.0)
factory_bot_rails (6.1.0)
factory_bot (~> 6.1.0)
railties (>= 5.0.0)
faker (2.15.1)
i18n (>= 1.6, < 2)
faraday (1.3.0)
faraday-net_http (~> 1.0)
multipart-post (>= 1.2, < 3)
fastimage (2.1.7)
ffi (1.11.1)
figaro (1.1.1)
thor (~> 0.14)
fugit (1.3.3)
ruby2_keywords
faraday-net_http (1.0.1)
fastimage (2.2.1)
ffi (1.14.2)
figaro (1.2.0)
thor (>= 0.14.0, < 2)
fugit (1.4.2)
et-orbi (~> 1.1, >= 1.1.8)
raabro (~> 1.1)
raabro (~> 1.4)
generator (0.0.1)
gherkin (5.1.0)
globalid (0.4.2)
activesupport (>= 4.2.0)
hammerjs-rails (2.0.8)
hashdiff (1.0.0)
hashie (3.6.0)
httparty (0.13.7)
json (~> 1.8)
hashdiff (1.0.1)
hashie (4.1.0)
httparty (0.17.3)
mime-types (~> 3.0)
multi_xml (>= 0.5.2)
i18n (1.6.0)
i18n (1.8.7)
concurrent-ruby (~> 1.0)
i18n-js (3.6.0)
i18n-js (3.8.0)
i18n (>= 0.6.6)
image_processing (1.9.3)
image_processing (1.12.1)
mini_magick (>= 4.9.5, < 5)
ruby-vips (>= 2.0.13, < 3)
iniparse (1.4.4)
jbuilder (2.9.1)
activesupport (>= 4.2.0)
ruby-vips (>= 2.0.17, < 3)
iniparse (1.5.0)
jbuilder (2.11.2)
activesupport (>= 5.0.0)
jmespath (1.4.0)
jquery-rails (4.3.5)
jquery-rails (4.4.0)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
@ -298,14 +320,14 @@ GEM
railties (>= 3.2.16)
js_cookie_rails (2.2.0)
railties (>= 3.1)
json (1.8.6)
json-jwt (1.11.0)
json (2.5.1)
json-jwt (1.13.0)
activesupport (>= 4.2)
aes_key_wrap
bindata
json_matchers (0.11.1)
json_schema
json_schema (0.20.7)
json_schema (0.20.9)
jsonapi-renderer (0.2.2)
jwt (1.5.6)
kaminari (1.2.1)
@ -321,95 +343,94 @@ GEM
kaminari-core (= 1.2.1)
kaminari-core (1.2.1)
lazy_priority_queue (0.1.1)
listen (3.1.5)
rb-fsevent (~> 0.9, >= 0.9.4)
rb-inotify (~> 0.9, >= 0.9.7)
ruby_dep (~> 1.2)
listen (3.4.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
little-plugger (1.1.4)
logging (2.0.0)
little-plugger (~> 1.1)
multi_json (~> 1.10)
loofah (2.5.0)
loofah (2.9.0)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
mail (2.7.1)
mini_mime (>= 0.1.1)
marcel (0.3.3)
mimemagic (~> 0.3.2)
method_source (0.9.2)
method_source (1.0.0)
mime-types (3.3.1)
mime-types-data (~> 3.2015)
mime-types-data (3.2020.0512)
mime-types-data (3.2020.1104)
mimemagic (0.3.5)
mini_magick (4.9.5)
mini_magick (4.11.0)
mini_mime (1.0.2)
mini_portile2 (2.5.0)
minitest (5.14.1)
minitest (5.14.3)
momentjs-rails (2.17.1)
railties (>= 3.1)
msgpack (1.3.1)
multi_json (1.13.1)
msgpack (1.4.2)
multi_json (1.15.0)
multi_test (0.1.2)
multi_xml (0.6.0)
multipart-post (2.1.1)
nested_form_fields (0.8.2)
nested_form_fields (0.8.4)
coffee-rails (>= 3.2.1)
jquery-rails
rails (>= 3.2.0)
newrelic_rpm (6.6.0.358)
nio4r (2.5.2)
nokogiri (1.11.0)
newrelic_rpm (6.15.0)
nio4r (2.5.4)
nokogiri (1.11.1)
mini_portile2 (~> 2.5.0)
racc (~> 1.4)
nokogumbo (2.0.2)
nokogumbo (2.0.4)
nokogiri (~> 1.8, >= 1.8.4)
oauth2 (1.4.1)
faraday (>= 0.8, < 0.16.0)
oauth2 (1.4.4)
faraday (>= 0.8, < 2.0)
jwt (>= 1.0, < 3.0)
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
omniauth (1.9.0)
hashie (>= 3.4.6, < 3.7.0)
omniauth (1.9.1)
hashie (>= 3.4.6)
rack (>= 1.6.2, < 3)
omniauth-azure-activedirectory (1.0.0)
jwt (~> 1.5)
omniauth (~> 1.1)
omniauth-linkedin-oauth2 (1.0.0)
omniauth-oauth2
omniauth-oauth2 (1.6.0)
oauth2 (~> 1.1)
omniauth (~> 1.9)
omniauth-oauth2 (1.7.1)
oauth2 (~> 1.4)
omniauth (>= 1.9, < 3)
omniauth-rails_csrf_protection (0.1.2)
actionpack (>= 4.2)
omniauth (>= 1.3.1)
orm_adapter (0.5.0)
overcommit (0.49.1)
childprocess (>= 0.6.3, < 2.0)
overcommit (0.57.0)
childprocess (>= 0.6.3, < 5)
iniparse (~> 1.4)
parallel (1.19.2)
parser (2.7.1.4)
parallel (1.20.1)
parser (3.0.0.0)
ast (~> 2.4.1)
pg (1.1.4)
pg_search (2.3.0)
activerecord (>= 4.2)
activesupport (>= 4.2)
pg (1.2.3)
pg_search (2.3.5)
activerecord (>= 5.2)
activesupport (>= 5.2)
polyglot (0.3.5)
pry (0.12.2)
coderay (~> 1.1.0)
method_source (~> 0.9.0)
pry-byebug (3.7.0)
pry (0.13.1)
coderay (~> 1.1)
method_source (~> 1.0)
pry-byebug (3.9.0)
byebug (~> 11.0)
pry (~> 0.10)
pry (~> 0.13.0)
pry-rails (0.3.9)
pry (>= 0.10.4)
public_suffix (4.0.1)
puma (4.3.5)
public_suffix (4.0.6)
puma (5.2.0)
nio4r (~> 2.0)
raabro (1.1.6)
raabro (1.4.0)
racc (1.5.2)
rack (2.2.3)
rack-attack (6.1.0)
rack-attack (6.4.0)
rack (>= 1.0, < 3)
rack-cors (1.1.1)
rack (>= 2.0.0)
@ -417,25 +438,25 @@ GEM
rack
rack-test (1.1.0)
rack (>= 1.0, < 3)
rails (6.0.3)
actioncable (= 6.0.3)
actionmailbox (= 6.0.3)
actionmailer (= 6.0.3)
actionpack (= 6.0.3)
actiontext (= 6.0.3)
actionview (= 6.0.3)
activejob (= 6.0.3)
activemodel (= 6.0.3)
activerecord (= 6.0.3)
activestorage (= 6.0.3)
activesupport (= 6.0.3)
bundler (>= 1.3.0)
railties (= 6.0.3)
rails (6.1.1)
actioncable (= 6.1.1)
actionmailbox (= 6.1.1)
actionmailer (= 6.1.1)
actionpack (= 6.1.1)
actiontext (= 6.1.1)
actionview (= 6.1.1)
activejob (= 6.1.1)
activemodel (= 6.1.1)
activerecord (= 6.1.1)
activestorage (= 6.1.1)
activesupport (= 6.1.1)
bundler (>= 1.15.0)
railties (= 6.1.1)
sprockets-rails (>= 2.0.0)
rails-controller-testing (1.0.4)
actionpack (>= 5.0.1.x)
actionview (>= 5.0.1.x)
activesupport (>= 5.0.1.x)
rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1)
activesupport (>= 5.0.1.rc1)
rails-dom-testing (2.0.3)
activesupport (>= 4.2.0)
nokogiri (>= 1.6)
@ -448,54 +469,53 @@ GEM
rails (> 3.1)
rails_serve_static_assets (0.0.5)
rails_stdout_logging (0.0.5)
railties (6.0.3)
actionpack (= 6.0.3)
activesupport (= 6.0.3)
railties (6.1.1)
actionpack (= 6.1.1)
activesupport (= 6.1.1)
method_source
rake (>= 0.8.7)
thor (>= 0.20.3, < 2.0)
thor (~> 1.0)
rainbow (3.0.0)
rake (12.3.3)
rb-fsevent (0.10.3)
rb-inotify (0.10.0)
rake (13.0.3)
rb-fsevent (0.10.4)
rb-inotify (0.10.1)
ffi (~> 1.0)
rdoc (6.2.0)
recaptcha (5.1.0)
rdoc (6.3.0)
recaptcha (5.6.0)
json
regexp_parser (1.6.0)
responders (3.0.0)
regexp_parser (2.0.3)
responders (3.0.1)
actionpack (>= 5.0)
railties (>= 5.0)
rexml (3.2.4)
rgl (0.5.6)
rgl (0.5.7)
lazy_priority_queue (~> 0.1.0)
stream (~> 0.5.2)
roo (2.8.2)
stream (~> 0.5.3)
roo (2.8.3)
nokogiri (~> 1)
rubyzip (>= 1.2.1, < 2.0.0)
rotp (6.0.0)
addressable (~> 2.7)
rqrcode (1.1.2)
rubyzip (>= 1.3.0, < 3.0.0)
rotp (6.2.0)
rqrcode (1.2.0)
chunky_png (~> 1.0)
rqrcode_core (~> 0.1)
rqrcode_core (0.1.2)
rspec-core (3.8.2)
rspec-support (~> 3.8.0)
rspec-expectations (3.8.4)
rqrcode_core (~> 0.2)
rqrcode_core (0.2.0)
rspec-core (3.10.1)
rspec-support (~> 3.10.0)
rspec-expectations (3.10.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-mocks (3.8.1)
rspec-support (~> 3.10.0)
rspec-mocks (3.10.1)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.8.0)
rspec-rails (4.0.0.beta2)
rspec-support (~> 3.10.0)
rspec-rails (4.0.2)
actionpack (>= 4.2)
activesupport (>= 4.2)
railties (>= 4.2)
rspec-core (~> 3.8)
rspec-expectations (~> 3.8)
rspec-mocks (~> 3.8)
rspec-support (~> 3.8)
rspec-support (3.8.2)
rspec-core (~> 3.10)
rspec-expectations (~> 3.10)
rspec-mocks (~> 3.10)
rspec-support (~> 3.10)
rspec-support (3.10.1)
rubocop (0.83.0)
parallel (~> 1.10)
parser (>= 2.7.0.1)
@ -503,25 +523,26 @@ GEM
rexml
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 2.0)
rubocop-performance (1.5.1)
rubocop (>= 0.71.0)
rubocop-rails (2.4.0)
rubocop-performance (1.7.1)
rubocop (>= 0.82.0)
rubocop-rails (2.6.0)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 0.72.0)
ruby-graphviz (1.2.4)
ruby-progressbar (1.10.1)
ruby-vips (2.0.15)
rubocop (>= 0.82.0)
ruby-graphviz (1.2.5)
rexml
ruby-progressbar (1.11.0)
ruby-vips (2.0.17)
ffi (~> 1.9)
ruby_dep (1.5.0)
ruby2_keywords (0.0.4)
rubyzip (1.3.0)
rufus-scheduler (3.6.0)
rufus-scheduler (3.7.0)
fugit (~> 1.1, >= 1.1.6)
safe_yaml (1.0.5)
sanitize (5.2.1)
sanitize (5.2.3)
crass (~> 1.0.2)
nokogiri (>= 1.8.0)
nokogumbo (~> 2.0)
sassc (2.2.1)
sassc (2.4.0)
ffi (~> 1.9)
sassc-rails (2.1.2)
railties (>= 4.0.0)
@ -529,55 +550,56 @@ GEM
sprockets (> 3.0)
sprockets-rails
tilt
scenic (1.5.1)
scenic (1.5.4)
activerecord (>= 4.0.0)
railties (>= 4.0.0)
sdoc (1.0.0)
sdoc (1.1.0)
rdoc (>= 5.0)
selenium-webdriver (3.142.4)
childprocess (>= 0.5, < 3.0)
rubyzip (~> 1.2, >= 1.2.2)
shoulda-matchers (4.1.2)
selenium-webdriver (3.142.7)
childprocess (>= 0.5, < 4.0)
rubyzip (>= 1.2.2)
shoulda-matchers (4.5.1)
activesupport (>= 4.2.0)
silencer (1.0.1)
simple_token_authentication (1.16.0)
actionmailer (>= 3.2.6, < 7)
actionpack (>= 3.2.6, < 7)
devise (>= 3.2, < 6)
simplecov (0.17.0)
simplecov (0.21.2)
docile (~> 1.1)
json (>= 1.8, < 3)
simplecov-html (~> 0.10.0)
simplecov-html (0.10.2)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.2)
spinjs-rails (1.4)
rails (>= 3.1)
sprockets (4.0.0)
sprockets (4.0.2)
concurrent-ruby (~> 1.0)
rack (> 1, < 3)
sprockets-rails (3.2.1)
sprockets-rails (3.2.2)
actionpack (>= 4.0)
activesupport (>= 4.0)
sprockets (>= 3.0.0)
stream (0.5.2)
thor (0.20.3)
thread_safe (0.3.6)
tilt (2.0.9)
timecop (0.9.1)
tinymce-rails (4.9.10)
stream (0.5.3)
generator
thor (1.1.0)
tilt (2.0.10)
timecop (0.9.2)
tinymce-rails (4.9.11)
railties (>= 3.1.1)
turbolinks (5.1.1)
turbolinks-source (~> 5.1)
turbolinks-source (5.2.0)
tzinfo (1.2.7)
thread_safe (~> 0.1)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
uglifier (4.2.0)
execjs (>= 0.3.0, < 3)
underscore-rails (1.8.3)
unicode-display_width (1.7.0)
uniform_notifier (1.12.1)
warden (1.2.8)
rack (>= 2.0.6)
webmock (3.7.2)
uniform_notifier (1.13.2)
warden (1.2.9)
rack (>= 2.0.9)
webmock (3.11.1)
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
@ -585,7 +607,7 @@ GEM
activesupport (>= 4.2)
rack-proxy (>= 0.6.1)
railties (>= 4.2)
websocket-driver (0.7.1)
websocket-driver (0.7.3)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
whacamole (1.2.0)
@ -594,7 +616,7 @@ GEM
wkhtmltopdf-heroku (2.12.5.0)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.3.0)
zeitwerk (2.4.2)
PLATFORMS
ruby
@ -639,7 +661,7 @@ DEPENDENCIES
fastimage
figaro
hammerjs-rails
httparty (~> 0.13.1)
httparty (~> 0.17.3)
i18n-js (~> 3.6)
image_processing (~> 1.2)
jbuilder
@ -671,7 +693,7 @@ DEPENDENCIES
puma
rack-attack
rack-cors
rails (~> 6.0.0)
rails (~> 6.1.1)
rails-controller-testing
rails_12factor
rails_autolink (~> 1.1, >= 1.1.6)
@ -712,7 +734,7 @@ DEPENDENCIES
yomu!
RUBY VERSION
ruby 2.6.4p104
ruby 2.7.2p137
BUNDLED WITH
1.17.3
2.1.4

View file

@ -1 +1 @@
1.21.1
1.21.2

Binary file not shown.

Before

Width:  |  Height:  |  Size: 538 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 531 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 231 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 530 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

View file

@ -1,18 +0,0 @@
# Content for tables used in demo project.
module1:
experimental_design_table: '{"data":[["group/time","1 dpi","6 dpi","",""],["PVYNTN","1","1","",""],["mock","1","1","",""],["","","","",""]]}'
module2:
samples_table: '{"data":[["Name","Sample type"],["Mock 1dpi","potatoe leaves"],["Mock 6dpi","potatoe leaves"],["PVY NTN 1dpi","potatoe leaves"],["PVY NTN 6dpi","potatoe leaves"]]}'
module3:
nanodrop: '{"data":[["Sample name","ng/ul ","260/280 ","260/230 "],["PVY NTN 6dpi","1.05","1.39","1.61"],["PVY NTN 1dpi","320.52","2.16","2.26"],["Mock 6dpi","906.19","2.15","2.26"],["Mock 1dpi","940.22","2.17","2.27"]]}'
module4:
volumes: '{"data":[["Amount of starting material (mg)","Volume of Buffer RLT (µl)"],["<20","350"],["20 - 30","600"]]}'
dna_q: '{"data":[["Sample 1","Quantity "],["1","0,2"],["2","0,5"],["3","0,4"],["4","1,1"],["5","12,5"],["6","0,05"],["7","0"],["8","0"],["9","0"],["10","0"],["11","0"],["12","4,7"],["13","2,3"],["14","5,6"],["15","2,6"],["16","8,9"],["17","1,7"],["18","1"]]}'
module6:
mastermix: '{"data":[["Date:","","","","","","","","","","","",""],["Reaction volume:","20 ul","","","","","","","","","","",""],["UMM/reaction:","10 µl","","","","","","","","","","",""],["sample/reaction:","6 µl","","","","","","","","","","",""],["","","","","","","","","","","","",""],["Chemistry","TaqMan","","","SYBR Green I","","","TaqMan","","","SYBR Green I","",""],["Gene","cox","","","cab4","","","PVY-NTN","","","PR-1","",""],["","conc.*","final conc.**","µl/reaction","conc.*","final conc.**","µl/reaction","conc.*","final conc.**","µl/reaction","conc.*","final conc.**","µl/reaction"],["Probe","10 µM","150 nM","0.3","/","/","/","5 µM","150 nM","0.6","/","/","/"],["FP","30 µM","300 nM","0.2","30 µM","300 nM ","0.2","10 µM","250 nM","0.5","30 µM","300 nM ","0.2"],["RP","30 µM","300 nM","0.2","30 µM","300 nM ","0.2","10 µM","250 nM","0.5","30 µM","300 nM ","0.2"]]}'
distribution: '{"data":[["%{sample0} (100x)\t","%{sample0} (100x)\t","%{sample0} (100x)\t","%{sample0} (100x)\t","%{sample0} (100x)\t","%{sample0} (100x)\t","%{sample0} (100x)\t","%{sample0} (100x)\t",null,null,"Mix smpl (10x)\t","Mix smpl (10x)\t"],["%{sample0} (1000x)\t","%{sample0} (1000x)\t","%{sample0} (1000x)\t","%{sample0} (1000x)\t","%{sample0} (1000x)\t","%{sample0} (1000x)\t","%{sample0} (1000x)\t","%{sample0} (1000x)\t",null,null,"Mix smpl (100x)\t","Mix smpl (100x)\t"],["%{sample1} (100x)\t","%{sample1} (100x)\t","%{sample1} (100x)\t","%{sample1} (100x)\t","%{sample1} (100x)\t","%{sample1} (100x)\t","%{sample1} (100x)\t","%{sample1} (100x)\t",null,null,"Mix smpl (1000x)\t","Mix smpl (1000x)\t"],["%{sample1} (1000x)\t","%{sample1} (1000x)\t","%{sample1} (1000x)\t","%{sample1} (1000x)\t","%{sample1} (1000x)\t","%{sample1} (1000x)\t","%{sample1} (1000x)\t","%{sample1} (1000x)\t",null,null,"Mix smpl (10000x)\t","Mix smpl (10000x)\t"],["%{sample2} (100x)\t","%{sample2} (100x)\t","%{sample2} (100x)\t","%{sample2} (100x)\t","%{sample2} (100x)\t","%{sample2} (100x)\t","%{sample2} (100x)\t","%{sample2} (100x)\t",null,null,"NTC1","NTC2"],["%{sample2} (1000x)\t","%{sample2} (1000x)\t","%{sample2} (1000x)\t","%{sample2} (1000x)\t","%{sample2} (1000x)\t","%{sample2} (1000x)\t","%{sample2} (1000x)\t","%{sample2} (1000x)\t",null,null,"NTC1","NTC2"],["%{sample3} (100x)\t","%{sample3} (100x)\t","%{sample3} (100x)\t","%{sample3} (100x)\t","%{sample3} (100x)\t","%{sample3} (100x)\t","%{sample3} (100x)\t","%{sample3} (100x)\t",null,null,"NTC1","NTC2"],["%{sample3} (1000x)\t","%{sample3} (1000x)\t","%{sample3} (1000x)\t","%{sample3} (1000x)\t","%{sample3} (1000x)\t","%{sample3} (1000x)\t","%{sample3} (1000x)\t","%{sample3} (1000x)\t",null,null,"NTC1","NTC2"],[null,null,null,null,null,null,null,null,null,null,null,null]]}'
qpcr_raw_data: '{"data":[["Sample 1","Quantity ","Column 3","Column 4"],["2","0,2","0,4","0,08"],["2","0,5","1","0,5"],["3","0,4","1,2","0,48"],["4","1,1","4,4","4,84"],["5","12,5","62,5","781,25"],["6","0,05","0,3","0,015"],["7","0","4","0"],["8","0","0","0"],["9","0","0","0"],["10","0","0","0"],["11","0","0","0"],["12","4,7","56,4","265,08"],["13","2,3","30","69"],["14","5,6","78,4","439,04"],["15","2,6","39","101,4"],["16","8,9","142,4","1267,36"],["17","1,7","28,9","49,13"],["18","1","18","18"],["","","",""]]}'
module7:
qpcr_results: '{"data":[["Sample 1","Quantity ",null,null,null],["1","0,2",null,null,null],["2","0,5",null,null,null],["3","0,4",null,null,null],["4","1,1",null,null,null],["5","12,5",null,null,null],["6","0,05",null,null,null],["7","0",null,null,null],["8","0",null,null,null],["9","0",null,null,null],["10","0",null,null,null],["11","0",null,null,null],["12","4,7",null,null,null],["13","2,3",null,null,null],["14","5,6",null,null,null],["15","2,6",null,null,null],["16","8,9",null,null,null],["17","1,7",null,null,null],["18","1",null,null,null]]}'

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View file

@ -1,54 +1,89 @@
(function(){
/* globals renderFormError animateSpinner */
(function() {
function clearModal(id) {
// Completely remove modal when it gets closed
$(id).on('hidden.bs.modal', function() {
$(id).remove();
});
}
// Reload after successfully updated experiment
function validateExperimentForm(element) {
if ($(element)) {
let form = $(element).find('form');
form.on('ajax:success', function(e, data) {
animateSpinner(form, true);
if (element.match(/#new-experiment-modal/)) {
window.location.replace(data.path);
} else {
location.reload();
}
}).on('ajax:error', function(e, error) {
let msg = JSON.parse(error.responseText);
if ('name' in msg) {
renderFormError(e, $(element).find('#experiment-name'), msg.name.toString(), true);
} else if ('description' in msg) {
renderFormError(e, $(element).find('#experiment-description'), msg.description.toString(), true);
} else {
renderFormError(e, $(element).find('#experiment-name'), error.statusText, true);
}
});
}
}
// Validates move action
function validateMoveModal(modal) {
if (modal.match(/#move-experiment-modal-[0-9]*/)) {
let form = $(modal).find('form');
form.on('ajax:success', function(e, data) {
animateSpinner(form, true);
window.location.replace(data.path);
}).on('ajax:error', function(e, error) {
form.clearFormErrors();
let msg = JSON.parse(error.responseText);
renderFormError(e, form.find('#experiment_project_id'), msg.message.toString());
});
}
}
// Create ajax hook on given 'element', which should return modal with 'id' =>
// show that modal
function initializeModal(element, id){
function initializeModal(element, id) {
// Initialize new experiment modal listener
$(element)
.on("ajax:beforeSend", function(){
$(element).on('ajax:beforeSend', function() {
animateSpinner();
})
.on("ajax:success", function(e, data){
}).on('ajax:success', function(e, data) {
$('body').append($.parseHTML(data.html));
$(id).modal('show',{
$(id).modal('show', {
backdrop: true,
keyboard: false,
keyboard: false
});
validateMoveModal(id);
clearModal($(id));
validateExperimentForm(id);
})
.on("ajax:error", function() {
}).on('ajax:error', function() {
animateSpinner(null, false);
// TODO
})
.on("ajax:complete", function(){
}).on('ajax:complete', function() {
animateSpinner(null, false);
$(id).find('.selectpicker').selectpicker();
});
}
function clearModal(id) {
//Completely remove modal when it gets closed
$(id).on('hidden.bs.modal', function() {
$(id).remove();
});
}
// Initialize dropdown actions on experiment:
// - edit
// - clone
function initializeDropdownActions(){
function initializeDropdownActions() {
// { buttonClass: modalName } mappings
// click on buttonClass summons modalName dialog
modals = {
let modals = {
'.edit-experiment': '#edit-experiment-modal-',
'.clone-experiment': '#clone-experiment-modal-',
'.move-experiment': '#move-experiment-modal-'
};
$.each($(".dropdown-experiment-actions"), function(){
$.each($('.dropdown-experiment-actions'), function() {
var $dropdown = $(this);
$.each(modals, function(buttonClass, modalName) {
var id = modalName + $dropdown.data('id');
@ -57,64 +92,11 @@
});
}
// Validates move action
function validateMoveModal(modal){
if ( modal.match(/#move-experiment-modal-[0-9]*/) ) {
var form = $(modal).find("form");
form
.on('ajax:success', function(e, data) {
animateSpinner(form, true);
window.location.replace(data.path);
})
.on('ajax:error', function(e, error) {
form.clearFormErrors();
var msg = JSON.parse(error.responseText);
renderFormError(e,
form.find("#experiment_project_id"),
msg.message.toString());
});
}
}
// Reload after successfully updated experiment
function validateExperimentForm(element){
if ( $(element) ) {
var form = $(element).find("form");
form
.on('ajax:success' , function(e, data){
animateSpinner(form, true);
if ( element.match(/#new-experiment-modal/) ) {
window.location.replace(data.path);
} else {
location.reload();
}
})
.on('ajax:error', function(e, error){
var msg = JSON.parse(error.responseText);
if ( 'name' in msg ) {
renderFormError(e,
$(element).find("#experiment-name"),
msg.name.toString(),
true);
} else if ( 'description' in msg ) {
renderFormError(e,
$(element).find("#experiment-description"),
msg.description.toString(),
true);
} else {
renderFormError(e,
$(element).find("#experiment-name"),
error.statusText,
true);
}
});
}
}
// Initialize no description edit link
function initEditNoDescription(){
function initEditNoDescription() {
var modal = '#edit-experiment-modal-';
$.each($('.experiment-no-description'), function() {
var id = modal + $(this).data("id");
var id = modal + $(this).data('id');
initializeModal($(this), id);
});
}
@ -140,4 +122,4 @@
// init
initEditNoDescription();
})();
}());

View file

@ -7,10 +7,10 @@
// - refresh project users tab after manage user modal is closed
// - refactor view handling using library, ex. backbone.js
/* global animateSpinner HelperModule dropdownSelector Sidebar Turbolinks */
/* global HelperModule dropdownSelector Sidebar Turbolinks filterDropdown */
(function() {
const PERMISSIONS = ['editable', 'archivable', 'restorable', 'moveable'];
var ProjectsIndex = (function() {
const PERMISSIONS = ['editable', 'archivable', 'restorable', 'moveable', 'deletable'];
var projectsWrapper = '#projectsWrapper';
var toolbarWrapper = '#toolbarWrapper';
var cardsWrapper = '#cardsWrapper';
@ -97,6 +97,42 @@
});
}
// init delete project folders
function initDeleteFoldersToolbarButton() {
$(projectsWrapper)
.on('ajax:before', '.delete-folders-btn', function() {
let buttonForm = $(this);
buttonForm.find('input[name="project_folders_ids[]"]').remove();
selectedProjectFolders.forEach(function(id) {
$('<input>').attr({
type: 'hidden',
name: 'project_folders_ids[]',
value: id
}).appendTo(buttonForm);
});
})
.on('ajax:success', '.delete-folders-btn', function(ev, data) {
// Add and show modal
let deleteModal = $(data.html);
$(projectsWrapper).append(deleteModal);
deleteModal.modal('show');
// Remove modal when it gets closed
deleteModal.on('hidden.bs.modal', function() {
$(this).remove();
});
});
$(projectsWrapper)
.on('ajax:success', '.delete-folders-form', function(ev, data) {
$('.modal-project-folder-delete').modal('hide');
HelperModule.flashAlertMsg(data.message, 'success');
refreshCurrentView();
})
.on('ajax:error', '.delete-folders-form', function(ev, data) {
HelperModule.flashAlertMsg(data.responseJSON.message, 'danger');
});
}
// init project toolbar archive/restore functions
function initArchiveRestoreToolbarButtons() {
$(projectsWrapper)
@ -237,6 +273,19 @@
});
}
// Initialize view project users modal remote loading.
function initViewProjectUsersLink() {
$(projectsWrapper).on('ajax:success', '.view-project-users-link', function(e, data) {
let viewProjectUsersModal = $(data.html);
$(projectsWrapper).append(viewProjectUsersModal);
viewProjectUsersModal.modal('show');
// Remove modal when it gets closed
viewProjectUsersModal.on('hidden.bs.modal', function() {
viewProjectUsersModal.remove();
});
});
}
// Initialize reloading manage user modal content after posting new
// user.
function initAddUserForm() {
@ -312,6 +361,8 @@
projectsToolbar.find('.single-object-action, .multiple-object-action').removeClass('hidden');
if (selectedProjectFolders.length === 1) {
projectsToolbar.find('.project-only-action').addClass('hidden');
} else {
projectsToolbar.find('.folders-only-action').addClass('hidden');
}
} else {
projectsToolbar.find('.single-object-action').addClass('hidden');
@ -319,6 +370,9 @@
if (selectedProjectFolders.length > 0) {
projectsToolbar.find('.project-only-action').addClass('hidden');
}
if (selectedProjects.length > 0) {
projectsToolbar.find('.folder-only-action').addClass('hidden');
}
}
PERMISSIONS.forEach((permission) => {
if (!checkActionPermission(permission)) {
@ -485,6 +539,7 @@
},
success: function(data) {
$('#breadcrumbsWrapper').html(data.breadcrumbs_html);
$(projectsWrapper).find('.projects-title').html(data.title);
$(toolbarWrapper).html(data.toolbar_html);
viewContainer.data('projects-cards-url', data.projects_cards_url);
viewContainer.removeClass('no-results');
@ -507,9 +562,9 @@
let projectsPageSelector = '.projects-index';
// list/cards switch
$(projectsPageSelector).on('click', '.projects-view-mode', function() {
$(projectsPageSelector).on('click', '.cards-switch', function() {
let $btn = $(this);
$('.projects-view-mode').removeClass('active');
$('.cards-switch').removeClass('active');
if ($btn.hasClass('view-switch-cards')) {
$(cardsWrapper).removeClass('list');
} else if ($btn.hasClass('view-switch-list')) {
@ -539,63 +594,36 @@
});
}
function appliedFiltersMark() {
let filtersEnabled = projectsViewSearch
|| createdOnFromFilter
|| createdOnToFilter
|| (membersFilter && membersFilter.length !== 0)
|| lookInsideFolders
|| archivedOnFromFilter
|| archivedOnToFilter;
if (filtersEnabled) {
$('.filter-container').addClass('filters-applied');
} else {
$('.filter-container').removeClass('filters-applied');
function selectDate($field) {
var datePicker = $field.data('DateTimePicker');
if (datePicker && datePicker.date()) {
return datePicker.date()._d.toUTCString();
}
return null;
}
function initProjectsFilters() {
function applyFilters() {
let teamId = $('.projects-filters').data('team-id');
projectsViewSearch = $('#textSearchFilterInput').closest('.select-block').find('input[type=text]').val();
try {
let storagePath = `project_filters_per_team/${teamId}/recent_search_keywords`;
let recentSearchKeywords = JSON.parse(localStorage.getItem(storagePath));
if (!Array.isArray(recentSearchKeywords)) recentSearchKeywords = [];
if (recentSearchKeywords.indexOf(projectsViewSearch) !== -1) {
recentSearchKeywords.splice(recentSearchKeywords.indexOf(projectsViewSearch), 1);
}
if (recentSearchKeywords.length > 4) {
recentSearchKeywords = recentSearchKeywords.slice(0, 4);
}
recentSearchKeywords.unshift(projectsViewSearch);
localStorage.setItem(storagePath, JSON.stringify(recentSearchKeywords));
} catch (error) {
console.error(error);
}
$('#applyProjectFiltersButton').closest('.dropdown').removeClass('open');
createdOnFromFilter = $createdOnFromFilter.val();
createdOnToFilter = $createdOnToFilter.val();
membersFilter = dropdownSelector.getValues($('.members-filter'));
lookInsideFolders = $foldersCB.prop('checked') ? 'true' : '';
archivedOnFromFilter = $archivedOnFromFilter.val();
archivedOnToFilter = $archivedOnToFilter.val();
appliedFiltersMark();
refreshCurrentView();
}
var $filterDropdown = filterDropdown.init();
let $projectsFilter = $('.projects-index .projects-filters');
let $membersFilter = $('.members-filter', $projectsFilter);
let $foldersCB = $('#folder_search', $projectsFilter);
let $createdOnFromFilter = $('#createdOnFromDate', $projectsFilter);
let $createdOnToFilter = $('#createdOnToDate', $projectsFilter);
let $archivedOnFromFilter = $('#archivedOnFromDate', $projectsFilter);
let $archivedOnToFilter = $('#archivedOnToDate', $projectsFilter);
let $createdOnFromFilter = $('.created-on-filter .from-date', $projectsFilter);
let $createdOnToFilter = $('.created-on-filter .to-date', $projectsFilter);
let $archivedOnFromFilter = $('.archived-on-filter .from-date', $projectsFilter);
let $archivedOnToFilter = $('.archived-on-filter .to-date', $projectsFilter);
let $textFilter = $('#textSearchFilterInput', $projectsFilter);
function appliedFiltersMark() {
let filtersEnabled = projectsViewSearch
|| createdOnFromFilter
|| createdOnToFilter
|| (membersFilter && membersFilter.length !== 0)
|| lookInsideFolders
|| archivedOnFromFilter
|| archivedOnToFilter;
filterDropdown.toggleFilterMark($filterDropdown, filtersEnabled);
}
dropdownSelector.init($membersFilter, {
optionClass: 'checkbox-icon users-dropdown-list',
optionLabel: (data) => {
@ -608,66 +636,30 @@
tagClass: 'users-dropdown-list'
});
$textFilter.click((e) => {
e.stopPropagation();
$('#textSearchFilterHistory').toggle();
$textFilter.closest('.dropdown').toggleClass('open');
}).on('input', () => {
$('#textSearchFilterHistory').hide();
$textFilter.closest('.dropdown').removeClass('open');
});
$projectsFilter.on('click', '.projects-search-keyword', function(e) {
e.stopPropagation();
e.preventDefault();
$textFilter.val($(this).data('keyword'));
$('#textSearchFilterHistory').hide();
$textFilter.closest('.dropdown').removeClass('open');
});
$projectsFilter.on('click', '#folderSearchInfoBtn', function(e) {
e.stopPropagation();
$('#folderSearchInfo').toggle();
});
$('.project-filters-dropdown').on('show.bs.dropdown', function() {
let teamId = $projectsFilter.data('team-id');
$('#textSearchFilterHistory').find('li').remove();
try {
let storagePath = `project_filters_per_team/${teamId}/recent_search_keywords`;
let recentSearchKeywords = JSON.parse(localStorage.getItem(storagePath));
$.each(recentSearchKeywords, function(i, keyword) {
$('#textSearchFilterHistory').append($(
`<li class="dropdown-item">
<a class="projects-search-keyword" href="#" data-keyword="${keyword}">
<i class="fas fa-history"></i>
<span class="keyword-text">${keyword}</span>
</a>
</li>`
));
});
} catch (error) {
console.error(error);
}
}).on('hide.bs.dropdown', function() {
$('#textSearchFilterHistory').hide();
$textFilter.closest('.dropdown').removeClass('open');
$('#folderSearchInfo').hide();
applyFilters();
$projectsFilter.on('click', '#folder_search', function(e) {
e.stopPropagation();
});
$('#applyProjectFiltersButton').click((e) => {
e.stopPropagation();
e.preventDefault();
applyFilters();
$filterDropdown.on('filter:apply', function() {
createdOnFromFilter = selectDate($createdOnFromFilter);
createdOnToFilter = selectDate($createdOnToFilter);
membersFilter = dropdownSelector.getValues($('.members-filter'));
lookInsideFolders = $foldersCB.prop('checked') ? 'true' : '';
archivedOnFromFilter = selectDate($archivedOnFromFilter);
archivedOnToFilter = selectDate($archivedOnToFilter);
projectsViewSearch = $textFilter.val();
appliedFiltersMark();
refreshCurrentView();
});
// Clear filters
$('.clear-button', $projectsFilter).click((e) => {
e.stopPropagation();
e.preventDefault();
$filterDropdown.on('filter:clear', function() {
dropdownSelector.clearData($membersFilter);
if ($createdOnFromFilter.data('DateTimePicker')) $createdOnFromFilter.data('DateTimePicker').clear();
if ($createdOnToFilter.data('DateTimePicker')) $createdOnToFilter.data('DateTimePicker').clear();
@ -678,15 +670,11 @@
});
// Prevent filter window close
$($projectsFilter).click((e) => {
if (!$(e.target).is('input,a')) {
e.stopPropagation();
e.preventDefault();
$('#textSearchFilterHistory').hide();
$textFilter.closest('.dropdown').removeClass('open');
dropdownSelector.closeDropdown($membersFilter);
$('#folderSearchInfo').hide();
}
$filterDropdown.on('filter:clickBody', function() {
$('#textSearchFilterHistory').hide();
$textFilter.closest('.dropdown').removeClass('open');
dropdownSelector.closeDropdown($membersFilter);
$('#folderSearchInfo').hide();
});
}
@ -705,7 +693,9 @@
initManageUsersModal();
initExportProjectsModal();
initExportProjects();
initDeleteFoldersToolbarButton();
initArchiveRestoreToolbarButtons();
initViewProjectUsersLink();
initManageProjectUsersLink();
initAddUserForm();
initRemoveUserLinks();
@ -756,4 +746,8 @@
}
init();
return {
loadCardsView: loadCardsView
};
}());

View file

@ -1,5 +1,276 @@
/* global animateSpinner filterDropdown Sidebar Turbolinks HelperModule */
(function() {
function init() {
const PERMISSIONS = ['editable', 'archivable', 'restorable', 'moveable', 'duplicable'];
var cardsWrapper = '#cardsWrapper';
var experimentsPage = '#projectShowWrapper';
var selectedExperiments = [];
let experimentsCurrentSort;
let experimentsViewSearch;
let startedOnFromFilter;
let startedOnToFilter;
let modifiedOnFromFilter;
let modifiedOnToFilter;
let archivedOnFromFilter;
let archivedOnToFilter;
function checkActionPermission(permission) {
return selectedExperiments.every(function(experimentId) {
return $(`.experiment-card[data-id="${experimentId}"]`).data(permission);
});
}
function updateExperimentsToolbar() {
let experimentsToolbar = $('#projectShowToolbar');
let toolbarVisible = false;
if (selectedExperiments.length === 0) {
experimentsToolbar.find('.single-object-action, .multiple-object-action').addClass('hidden');
}
if (selectedExperiments.length === 1) {
experimentsToolbar.find('.single-object-action, .multiple-object-action').removeClass('hidden');
} else if (selectedExperiments.length > 1) {
experimentsToolbar.find('.single-object-action').addClass('hidden');
experimentsToolbar.find('.multiple-object-action').removeClass('hidden');
}
PERMISSIONS.forEach((permission) => {
if (!checkActionPermission(permission)) {
experimentsToolbar.find(`.btn[data-for="${permission}"]`).addClass('hidden');
}
});
$.each($('#projectShowToolbar').find('.btn'), (i, btn) => {
if (window.getComputedStyle(btn).display !== 'none') {
toolbarVisible = true;
}
});
$(experimentsPage).attr('data-toolbar-visible', toolbarVisible);
}
function initProjectsViewModeSwitch() {
$(experimentsPage).on('click', '.cards-switch', function() {
let $btn = $(this);
$('.cards-switch').removeClass('active');
if ($btn.hasClass('view-switch-cards')) {
$(cardsWrapper).removeClass('list');
} else if ($btn.hasClass('view-switch-list')) {
$(cardsWrapper).addClass('list');
}
$btn.addClass('active');
});
$(experimentsPage).on('click', '.archive-switch', function() {
Turbolinks.visit($(this).data('url'));
});
}
function loadCardsView() {
var viewContainer = $(cardsWrapper);
$.ajax({
url: viewContainer.data('experiments-cards-url'),
type: 'GET',
dataType: 'json',
data: {
view_mode: $(experimentsPage).data('view-mode'),
sort: experimentsCurrentSort,
search: experimentsViewSearch,
created_on_from: startedOnFromFilter,
created_on_to: startedOnToFilter,
updated_on_from: modifiedOnFromFilter,
updated_on_to: modifiedOnToFilter,
archived_on_from: archivedOnFromFilter,
archived_on_to: archivedOnToFilter
},
success: function(data) {
viewContainer.find('.card, .no-results-container').remove();
viewContainer.removeClass('no-results');
viewContainer.append(data.cards_html);
if (viewContainer.find('.no-results-container').length) {
viewContainer.addClass('no-results');
}
selectedExperiments.length = 0;
updateExperimentsToolbar();
loadExperimentWorkflowImages();
},
error: function() {
viewContainer.html('Error loading project list');
}
});
}
function refreshCurrentView() {
loadCardsView();
Sidebar.reload({
sort: experimentsCurrentSort,
view_mode: $(experimentsPage).data('view-mode')
});
}
function selectDate($field) {
var datePicker = $field.data('DateTimePicker');
if (datePicker && datePicker.date()) {
return datePicker.date()._d.toUTCString();
}
return null;
}
function initExperimentsFilters() {
var $filterDropdown = filterDropdown.init();
let $experimentsFilter = $('.experiments-filters');
let $startedOnFromFilter = $('.started-on-filter .from-date', $experimentsFilter);
let $startedOnToFilter = $('.started-on-filter .to-date', $experimentsFilter);
let $modifiedOnFromFilter = $('.modified-on-filter .from-date', $experimentsFilter);
let $modifiedOnToFilter = $('.modified-on-filter .to-date', $experimentsFilter);
let $archivedOnFromFilter = $('.archived-on-filter .from-date', $experimentsFilter);
let $archivedOnToFilter = $('.archived-on-filter .to-date', $experimentsFilter);
let $textFilter = $('#textSearchFilterInput', $experimentsFilter);
function appliedFiltersMark() {
let filtersEnabled = experimentsViewSearch
|| startedOnFromFilter
|| startedOnToFilter
|| modifiedOnFromFilter
|| modifiedOnToFilter
|| archivedOnFromFilter
|| archivedOnToFilter;
filterDropdown.toggleFilterMark($filterDropdown, filtersEnabled);
}
$filterDropdown.on('filter:apply', function() {
startedOnFromFilter = selectDate($startedOnFromFilter);
startedOnToFilter = selectDate($startedOnToFilter);
modifiedOnFromFilter = selectDate($modifiedOnFromFilter);
modifiedOnToFilter = selectDate($modifiedOnToFilter);
archivedOnFromFilter = selectDate($archivedOnFromFilter);
archivedOnToFilter = selectDate($archivedOnToFilter);
experimentsViewSearch = $textFilter.val();
appliedFiltersMark();
refreshCurrentView();
});
// Clear filters
$filterDropdown.on('filter:clear', function() {
if ($startedOnFromFilter.data('DateTimePicker')) $startedOnFromFilter.data('DateTimePicker').clear();
if ($startedOnToFilter.data('DateTimePicker')) $startedOnToFilter.data('DateTimePicker').clear();
if ($modifiedOnFromFilter.data('DateTimePicker')) $modifiedOnFromFilter.data('DateTimePicker').clear();
if ($modifiedOnToFilter.data('DateTimePicker')) $modifiedOnToFilter.data('DateTimePicker').clear();
if ($archivedOnFromFilter.data('DateTimePicker')) $archivedOnFromFilter.data('DateTimePicker').clear();
if ($archivedOnToFilter.data('DateTimePicker')) $archivedOnToFilter.data('DateTimePicker').clear();
$textFilter.val('');
});
}
function initSorting() {
$('#sortMenuDropdown a').click(function() {
if (experimentsCurrentSort !== $(this).data('sort')) {
$('#sortMenuDropdown a').removeClass('selected');
experimentsCurrentSort = $(this).data('sort');
refreshCurrentView();
$(this).addClass('selected');
$('#sortMenu').dropdown('toggle');
}
});
}
function initExperimentsSelector() {
$(cardsWrapper).on('click', '.experiment-card-selector', function() {
let card = $(this).closest('.experiment-card');
let experimentId = card.data('id');
// Determine whether ID is in the list of selected project IDs
let index = $.inArray(experimentId, selectedExperiments);
// If checkbox is checked and row ID is not in list of selected project IDs
if (this.checked && index === -1) {
$(this).closest('.experiment-card').addClass('selected');
selectedExperiments.push(experimentId);
// Otherwise, if checkbox is not checked and ID is in list of selected IDs
} else if (!this.checked && index !== -1) {
$(this).closest('.experiment-card').removeClass('selected');
selectedExperiments.splice(index, 1);
}
updateExperimentsToolbar();
});
}
function initSelectAllCheckbox() {
$(experimentsPage).on('click', '.sci-checkbox.select-all', function() {
var selectAll = this.checked;
$.each($('.experiment-card-selector'), function() {
if (this.checked !== selectAll) this.click();
});
});
}
function initArchiveRestoreToolbarButtons() {
$(experimentsPage)
.on('ajax:before', '.archive-experiments-form, .restore-experiments-form', function() {
let buttonForm = $(this);
buttonForm.find('input[name="experiments_ids[]"]').remove();
selectedExperiments.forEach(function(id) {
$('<input>').attr({
type: 'hidden',
name: 'experiments_ids[]',
value: id
}).appendTo(buttonForm);
});
})
.on('ajax:success', '.archive-experiments-form, .restore-experiments-form', function(ev, data) {
HelperModule.flashAlertMsg(data.message, 'success');
// Project saved, reload view
refreshCurrentView();
})
.on('ajax:error', '.archive-experiments-form, .restore-experiments-form', function(ev, data) {
HelperModule.flashAlertMsg(data.responseJSON.message, 'danger');
});
}
function appendActionModal(modal) {
$('#content-wrapper').append(modal);
modal.modal('show');
modal.find('.selectpicker').selectpicker();
// Remove modal when it gets closed
modal.on('hidden.bs.modal', function() {
$(this).remove();
});
}
function initEditMoveDuplicateToolbarButton() {
let forms = '.edit-experiments-form, .move-experiments-form, .clone-experiments-form';
$(experimentsPage)
.on('ajax:before', forms, function() {
let buttonForm = $(this);
buttonForm.find('input[name="id"]').remove();
$('<input>').attr({
type: 'hidden',
name: 'id',
value: selectedExperiments[0]
}).appendTo(buttonForm);
})
.on('ajax:success', forms, function(ev, data) {
appendActionModal($(data.html));
})
.on('ajax:error', forms, function(ev, data) {
HelperModule.flashAlertMsg(data.responseJSON.message, 'danger');
});
}
function initNewExperimentToolbarButton() {
let forms = '.new-experiment-form';
$(experimentsPage)
.on('ajax:success', forms, function(ev, data) {
appendActionModal($(data.html));
})
.on('ajax:error', forms, function(ev, data) {
HelperModule.flashAlertMsg(data.responseJSON.message, 'danger');
});
}
function loadExperimentWorkflowImages() {
$('.workflowimg-container').each(function() {
let container = $(this);
if (container.data('workflowimg-present') === false) {
@ -20,5 +291,35 @@
});
}
function init() {
$('#content-wrapper').on('ajax:success', '.experiment-action-link', function(ev, data) {
appendActionModal($(data.html));
});
$('#content-wrapper')
.on('ajax:beforeSend', '.experiment-action-form', function() {
animateSpinner();
})
.on('ajax:success', '.experiment-action-form', function() {
$(this).closest('.modal').modal('hide');
refreshCurrentView();
animateSpinner(null, false);
})
.on('ajax:error', '.experiment-action-form', function(ev, data) {
animateSpinner(null, false);
$(this).renderFormErrors('experiment', data.responseJSON);
});
initExperimentsFilters();
initSorting();
loadCardsView();
initProjectsViewModeSwitch();
initExperimentsSelector();
initArchiveRestoreToolbarButtons();
initEditMoveDuplicateToolbarButton();
initNewExperimentToolbarButton();
initSelectAllCheckbox();
}
init();
}());

View file

@ -0,0 +1,116 @@
var filterDropdown = (function() {
var $filterContainer = '';
function initClearButton() {
$('.clear-button', $filterContainer).click(function(e) {
e.stopPropagation();
e.preventDefault();
$filterContainer.trigger('filter:clear');
});
}
function preventDropdownClose() {
$filterContainer.on('click', '.dropdown-menu', function(e) {
if (!$(e.target).is('input,a')) {
e.stopPropagation();
e.preventDefault();
$('#textSearchFilterHistory').hide();
$('#textSearchFilterInput', $filterContainer).closest('.dropdown').removeClass('open');
$filterContainer.trigger('filter:clickBody');
}
});
}
function initSearchField() {
var $textFilter = $('#textSearchFilterInput', $filterContainer);
$filterContainer.on('show.bs.dropdown', function() {
let $filterDropdown = $filterContainer.find('.dropdown-menu');
let teamId = $filterDropdown.data('team-id');
$('#textSearchFilterHistory').find('li').remove();
try {
let storagePath = `${$filterDropdown.data('search-field-history-key')}/${teamId}/recent_search_keywords`;
let recentSearchKeywords = JSON.parse(localStorage.getItem(storagePath));
$.each(recentSearchKeywords, function(i, keyword) {
$('#textSearchFilterHistory').append(
$(`<li class="dropdown-item">
<a class="projects-search-keyword" href="#" data-keyword="${keyword}">
<i class="fas fa-history"></i>
<span class="keyword-text">${keyword}</span>
</a>
</li>`)
);
});
} catch (error) {
console.error(error);
}
}).on('hide.bs.dropdown', function() {
$('#textSearchFilterHistory').hide();
$('.apply-filters', $filterContainer).click();
});
$textFilter.click(function(e) {
e.stopPropagation();
$('#textSearchFilterHistory').toggle();
$(this).closest('.dropdown').toggleClass('open');
}).on('input', () => {
$('#textSearchFilterHistory').hide();
$(this).closest('.dropdown').removeClass('open');
});
$filterContainer.on('click', '.projects-search-keyword', function(e) {
e.stopPropagation();
e.preventDefault();
$textFilter.val($(this).data('keyword'));
$('#textSearchFilterHistory').hide();
$textFilter.closest('.dropdown').removeClass('open');
});
}
function initApplyButton() {
$('.apply-filters', $filterContainer).click(function(e) {
e.stopPropagation();
e.preventDefault();
let $filterDropdown = $filterContainer.find('.dropdown-menu');
let teamId = $filterDropdown.data('team-id');
let projectsViewSearch = $('#textSearchFilterInput').val();
try {
let storagePath = `${$filterDropdown.data('search-field-history-key')}/${teamId}/recent_search_keywords`;
let recentSearchKeywords = JSON.parse(localStorage.getItem(storagePath));
if (!Array.isArray(recentSearchKeywords)) recentSearchKeywords = [];
if (recentSearchKeywords.indexOf(projectsViewSearch) !== -1) {
recentSearchKeywords.splice(recentSearchKeywords.indexOf(projectsViewSearch), 1);
}
if (recentSearchKeywords.length > 4) {
recentSearchKeywords = recentSearchKeywords.slice(0, 4);
}
recentSearchKeywords.unshift(projectsViewSearch);
localStorage.setItem(storagePath, JSON.stringify(recentSearchKeywords));
} catch (error) {
console.error(error);
}
$filterContainer.trigger('filter:apply');
$(this).closest('.dropdown').removeClass('open');
});
}
return {
init: function() {
$filterContainer = $('.filter-container');
initClearButton();
preventDropdownClose();
initApplyButton();
initSearchField();
return $filterContainer;
},
toggleFilterMark: function(filterContainer, filtersEnabled) {
if (filtersEnabled) {
filterContainer.addClass('filters-applied');
} else {
filterContainer.removeClass('filters-applied');
}
}
};
}());

View file

@ -5,7 +5,6 @@
.dashboard-container .calendar-widget {
--calendar-day-size: 32px;
min-height: 320px;
.dashboard-calendar {
height: 100%;
@ -40,9 +39,9 @@
flex-basis: calc(100% - 42px);
flex-grow: 1;
grid-column-gap: 6px;
grid-row-gap: 6px;
grid-row-gap: 2px;
grid-template-columns: repeat(7, 1fr);
grid-template-rows: repeat(7, 1fr);
grid-template-rows: repeat(auto-fit, minmax(1em, 1fr));
justify-items: center;
padding: 6px;

View file

@ -62,6 +62,7 @@
.filter-container {
height: 36px;
margin-right: 4px;
position: relative;
width: 36px;
.current-tasks-filters {

View file

@ -12,19 +12,20 @@
.dashboard-view {
--dashboard-widgets-gap: 30px;
padding: calc(var(--dashboard-widgets-gap) / 2)
calc(var(--dashboard-widgets-gap) / 2)
padding: 15px
var(--dashboard-widgets-gap)
15px
var(--dashboard-widgets-gap);
}
.dashboard-header {
padding-bottom: calc(var(--dashboard-widgets-gap) / 2);
padding-bottom: 15px;
}
.dashboard-container {
--widget-header-size: 44px;
display: grid;
grid-auto-rows: 30em;
grid-auto-rows: 26em;
grid-column-gap: var(--dashboard-widgets-gap);
grid-row-gap: var(--dashboard-widgets-gap);
grid-template-columns: repeat(auto-fit, minmax(7em, 1fr));
@ -99,20 +100,21 @@
@media (max-height: 800px) {
.dashboard-container {
grid-auto-rows: 24em;
grid-auto-rows: 20em;
}
.dashboard-view {
--dashboard-widgets-gap: 15px;
}
}
@media (min-height: 1300px) {
@media (min-height: 1080px) {
.dashboard-container {
grid-auto-rows: 36em;
grid-auto-rows: 32em;
}
}
@media (max-width: 700px) {
.dashboard-view {
--dashboard-widgets-gap: 15px;
}
.dashboard-container {
--widget-header-size: 72px;

View file

@ -3,207 +3,356 @@
// scss-lint:disable SelectorFormat
// scss-lint:disable IdSelector
// EXPERIMENT PANEL
.experiment-panel {
@include box-shadow(0 4px 8px 0 $color-dove-gray);
display: flex;
flex-direction: column;
height: 400px;
margin: 0 auto;
margin-bottom: 35px;
margin-top: 15px;
max-width: 700px;
.panel-title {
margin-bottom: 14px;
}
// New experiments page
.panel-heading .clone-experiment,
.panel-heading .dropdown {
display: none;
}
.projects-show {
.experiment-actions-menu {
.dropdown-menu {
a,
button {
@include font-button;
padding: .5em 1em;
&:hover .clone-experiment,
&:hover .dropdown {
display: block;
}
.panel-date {
color: $color-silver-chalice;
.fas {
display: inline-block;
width: 1em;
.fas {
margin-right: .4em;
}
}
}
}
.panel-heading > .clone-experiment {
color: $color-silver-chalice;
padding-left: 4px;
padding-top: 2px;
}
.dropdown {
color: $color-silver-chalice;
button {
padding-left: 6px;
}
}
.panel-body {
display: flex;
flex: 1 1 auto;
flex-direction: column;
height: 100%;
a {
height: 100%;
}
}
.experiment-description {
margin-top: 10px;
max-height: 86px;
overflow-x: hidden;
p {
margin: 0;
}
}
.experiment-no-description {
@include font-h3;
color: $color-alto;
display: block;
font-weight: bold;
margin-top: 10px;
text-align: center;
}
.no-workflowimg {
color: $color-alto;
display: block;
font-size: 22px;
font-weight: bold;
margin: 15px 0;
max-height: 200px;
padding-bottom: 70px;
padding-top: 50px;
text-align: center;
}
}
.workflowimg-container {
align-items: center;
display: flex;
height: 100%;
justify-content: center;
img {
max-height: 190px;
max-width: 100%;
}
}
// SHOW
.center-btn {
display: block;
margin: 0 auto;
max-width: 200px;
}
.big-plus {
color: $color-gainsboro;
display: block;
font-size: 180px;
margin: 20px 0;
text-align: center;
&:hover {
color: $color-alto;
}
&:focus {
color: $color-alto;
}
}
#project-show-archive {
.experiment-panel {
background: $color-concrete;
.panel-heading {
background: unset;
}
.panel-date {
color: $color-black;
}
}
}
#project-show,
#project-show-archive {
.content-header {
.project-name {
max-width: calc(100% - 2em);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
align-items: center;
display: flex;
max-width: calc(100% - 7em);
.fas {
margin-right: .5em;
}
}
}
.header-actions {
margin-left: auto;
.experiment-sort-menu {
.edit-experiments-form,
.clone-experiments-form,
.move-experiments-form,
.archive-experiments-form,
.new-experiment-form,
.restore-experiments-form {
display: inline-block;
}
}
#sortMenu {
background: $color-white;
.project-show-container {
margin: 1.5em 0;
&:focus,
&:active {
box-shadow: none;
}
.cards-wrapper {
--card-min-width: 350px;
--list-columns-number: 7;
&:hover {
background: $color-concrete;
.card {
grid-row: span 6;
&.experiment-card {
border-radius: 4px;
box-shadow: $flyout-shadow;
.workflow-img-wrapper {
background-color: $color-concrete;
border-radius: 4px;
height: 76px;
width: 76px;
.archived-icon-plceholder {
color: $color-silver-chalice;
font-size: 3.5em;
line-height: 76px;
text-align: center;
}
.workflowimg-container {
text-align: center;
img {
border-radius: 4px;
max-height: 76px;
}
}
}
.experiment-name-cell {
@include font-h3;
-webkit-box-orient: vertical;
display: -webkit-box;
height: 3em;
-webkit-line-clamp: 2;
margin: .25em 1.75em;
overflow: hidden;
text-overflow: ellipsis;
width: 100%;
a {
color: inherit;
}
}
.actions {
position: absolute;
right: .2em;
top: .2em;
}
.dates-and-img-container {
display: flex;
height: 7em;
width: 100%;
.dates-container {
flex-grow: 1;
}
}
.data-row {
display: flex;
line-height: 34px;
.card-label {
width: 7em;
}
.card-value {
font-weight: 600;
}
}
.progress {
height: 4px;
margin-bottom: 0;
width: 100%;
.progress-bar {
min-width: 0 !important;
}
}
.completed-task-cell {
width: 100%;
}
.description-cell {
.description-text {
-webkit-box-orient: vertical;
display: -webkit-box;
height: 3em;
-webkit-line-clamp: 2;
overflow: hidden;
padding-top: .25em;
&::after {
background: linear-gradient(to right, transparent, $color-white 50%);
bottom: .75em;
content: "";
height: 1.75em;
position: absolute;
right: 0;
text-align: right;
width: 25%;
}
}
.more-button {
bottom: .8em;
position: absolute;
right: 1em;
}
}
}
}
.dropdown-menu {
@include font-button;
min-width: 200px;
padding: .5em 0;
&.list {
grid-auto-rows: 1px 5em;
grid-template-columns: max-content repeat(calc(var(--list-columns-number) - 2), minmax(100px, auto)) max-content;
grid-template-rows: 3em;
a {
border-radius: unset;
cursor: pointer;
padding: .5em 1em;
text-align: left;
.card {
&.experiment-card {
.card-value {
font-weight: normal;
line-height: 2em;
}
.workflow-img-wrapper {
flex-shrink: 0;
height: 3.5em;
margin: .25em 1em .25em .5em;
width: 3.5em;
.archived-icon-plceholder {
font-size: 2em;
line-height: 1.75em;
}
.workflowimg-container {
text-align: center;
img {
max-height: 3em;
}
}
}
.dates-and-img-container,
.dates-container {
display: contents;
}
.checkbox-cell {
align-items: normal;
padding-top: .5em;
}
.experiment-name-cell {
@include font-button;
color: $brand-primary;
display: flex;
font-weight: normal;
grid-column: 2;
height: 100%;
margin: 0;
padding: .25em 0;
a {
overflow: hidden;
}
}
.start-date-cell {
grid-column: 3;
}
.modified-date-cell {
grid-column: 4;
}
.completed-task-cell {
grid-column: 5;
}
.description-cell {
grid-column: 6;
position: relative;
.description-text {
height: 4.5em;
-webkit-line-clamp: 3;
&::after {
bottom: .5em;
right: .5em;
}
}
.more-button {
bottom: .5em;
}
}
.actions-cell {
grid-column: 7;
padding-top: 3px;
position: initial;
}
}
&:hover {
background: $color-concrete;
.description-text::after {
background: linear-gradient(to right, transparent, $color-concrete 50%);
}
}
}
}
&.selected::after {
@include font-awesome;
content: $font-fas-check;
margin-left: auto;
position: absolute;
right: 1em;
}
&.readonly {
.experiment-name-cell {
margin-left: 0 !important;
}
}
}
}
.project-show-toolbar {
margin-top: 1em;
&.active {
[data-view-mode="archived"] {
display: none !important;
}
}
&.archived {
[data-view-mode="active"] {
display: none !important;
}
.project-show-container {
.experiment-actions-menu {
.btn-light:hover {
background: $color-alto;
}
}
.cards-wrapper {
.card.experiment-card {
.workflow-img-wrapper {
background-color: $color-alto;
}
.progress-bar {
background-color: $color-silver-chalice;
}
.description-cell {
width: 100%;
.description-text::before {
background: $color-alto;
content: "";
display: block;
height: 4px;
}
.description-text::after {
background: linear-gradient(to right, transparent, $color-concrete 50%);
}
}
}
&.list {
.card.experiment-card {
.archived-date-cell {
grid-column: 5;
}
.description-cell {
.description-text::before {
content: unset;
}
}
&:hover {
.description-text::after {
background: linear-gradient(to right, transparent, $color-alto 50%);
}
}
}
}
}
}
}
}
@media (max-height: 700px) {
.projects-show {
.experiments-filters {
max-height: calc(100vh - var(--navbar-height) - var(--content-header-size));
overflow: auto;
}
}
}

View file

@ -13,14 +13,6 @@
font-size: $font-size-base;
padding: 0 !important;
.preview-close {
background: transparent;
border: 0;
color: $color-white;
display: inline-block;
float: right;
}
.modal-dialog {
height: 100%;
margin: 0;
@ -32,14 +24,12 @@
background: transparent;
border: 0;
box-shadow: none;
color: $color-white;
height: 100%;
width: auto;
}
.modal-header {
background: $color-black;
border: 0;
background: $color-white;
display: flex;
height: 60px;
line-height: 40px;
@ -123,14 +113,6 @@
}
}
}
.file-save-link {
color: $color-white;
cursor: pointer;
display: inline-block;
float: right;
margin-right: 20px;
}
}
#new-step-sketch {

View file

@ -8,20 +8,6 @@
$color-group-hover: $color-alabaster;
$color-module-hover: $brand-primary;
// Breadcrumbs
.projects-breadcrumbs {
padding: .5em 1em;
}
/* Secondary navigation */
.navbar-nav {
.projects-view-mode-switch {
margin: 8px 10px 12px;
}
}
/* Canvas index page */
#canvas-container:not(.canvas-container-edit-mode) {
@ -396,7 +382,6 @@ li.module-hover {
/* Edit module tags modal window */
#manage-module-tags-modal {
.add-tag-form {
display: inline-block;
margin-left: 15px;
@ -468,176 +453,36 @@ li.module-hover {
}
// New projects page
/// icon override
#breadcrumbsWrapper {
background: $color-white;
height: 1em;
margin-left: -2em;
padding: 0 2em;
width: calc(100% + 4em);
.panel-project {
.nav-tabs[data-hook="project-footer-icons"] {
.btn-link {
display: inline-block;
min-width: 30px;
}
.badge-indicator {
background: transparent;
color: $color-silver-chalice;
font-size: 12px;
margin-left: 0;
padding: 0;
top: 0;
}
}
.tab-content[data-hook="project-dropdown-panel"] {
.over-due {
.title {
border-top: 1px solid $color-concrete;
font-size: 14px;
line-height: 15px;
padding: 10px 15px 0;
}
.notification {
padding-left: 35px;
.date-time {
position: relative;
.fas {
font-size: 14px;
left: -20px;
position: absolute;
top: 2px;
}
}
}
}
.projects-breadcrumbs {
@include font-small;
padding: .75em 0;
}
}
// New projects page
.projects-index {
.cards-wrapper {
align-items: center;
display: grid;
grid-auto-rows: 2em;
grid-column-gap: 1em;
grid-row-gap: 1em;
grid-template-columns: repeat(auto-fill, minmax(291px, 1fr));
width: 100%;
.table-header {
display: none;
.project-users-list {
hr {
margin: .5em 0;
}
}
.projects-view-mode {
&.active::after {
@include font-awesome;
content: "\f00c";
position: absolute;
right: 1em;
}
}
.archive-projects-form {
.delete-folders-form,
.delete-folders-btn {
display: inline-block;
}
.restore-projects-form {
display: inline-block;
}
.header-actions {
align-items: center;
display: flex;
margin-left: auto;
.projects-sort-menu {
display: inline-block;
#sortMenu {
background: $color-white;
&:focus,
&:active {
box-shadow: none;
}
&:hover {
background: $color-concrete;
}
}
.dropdown-menu {
@include font-button;
min-width: 150px;
padding: .5em 0;
a {
border-radius: unset;
cursor: pointer;
padding: .5em 1em;
text-align: left;
&:hover {
background: $color-concrete;
}
&.selected::after {
@include font-awesome;
content: $font-fas-check;
margin-left: auto;
position: absolute;
right: 1em;
}
}
}
}
}
.filter-container {
position: initial;
.projects-filters {
padding: 0;
width: 230px;
.header {
align-items: center;
border-bottom: $border-default;
display: flex;
height: 2.75em;
margin-bottom: 1em;
padding: 0 .3em 0 1em;
.title {
@include font-h2;
flex-grow: 1;
user-select: none;
}
}
.select-block {
display: inline-block;
padding: 0 1em 1em;
position: relative;
width: 100%;
label {
@include font-small;
font-weight: bold;
margin-bottom: .3em;
user-select: none;
}
&.folders {
align-items: center;
display: flex;
@ -661,94 +506,6 @@ li.module-hover {
}
}
}
.text-search-filter {
&.open {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
.recent-searches {
border-top-left-radius: 0;
border-top-right-radius: 0;
left: auto;
margin-top: -1px;
top: auto;
width: calc(100% - 2em);
label {
@include font-small;
color: $color-silver-chalice;
font-weight: bold;
margin-bottom: .3em;
padding: 0 1.5em;
user-select: none;
}
.projects-search-keyword {
@include font-button;
padding: .5em 1.25em;
.keyword-text {
margin-left: .25em;
}
}
}
.footer {
align-items: center;
border-top: $border-default;
display: flex;
height: 4.25em;
justify-content: center;
padding: 0 1em;
position: relative;
width: 100%;
}
}
&.filters-applied .filter-button::after {
@include red-dot;
right: .35em;
top: .3em;
}
.connect-line {
background: $input-border;
height: 10px;
margin: 5px auto;
width: 2px;
}
@media (min-width: 768px) {
.projects-filters {
width: 460px;
}
.footer {
.btn:last-child {
margin-left: auto;
}
}
.datetime-picker-container {
display: inline-block;
width: 46%;
}
.connect-line {
display: inline-block;
margin: 0 11px;
position: relative;
top: 2px;
transform: rotate(90deg);
}
.created-on-label {
display: block;
}
}
.users-dropdown-list {
@ -762,56 +519,25 @@ li.module-hover {
}
.projects-toolbar {
margin-bottom: 1em;
.form-group {
width: 100%;
}
}
.archive-projects-form {
display: inline-block;
}
.restore-projects-form {
display: inline-block;
}
}
.projects-container {
margin: 1.5em 20px;
margin: 1.5em 0;
.cards-wrapper {
align-items: center;
display: grid;
grid-auto-rows: 2em;
grid-column-gap: 1em;
grid-row-gap: 1em;
grid-template-columns: repeat(auto-fill, minmax(291px, 1fr));
width: 100%;
&.no-results {
.no-results-container {
grid-row: 8;
grid-column: 1 / -1;
}
.no-results-img {
display: block;
margin: auto;
max-height: 230px;
}
.no-results-title {
@include font-h1;
margin-bottom: .25em;
margin-top: 1.25em;
text-align: center;
}
.no-results-description {
@include font-main;
color: $color-silver-chalice;
text-align: center;
}
}
.table-header {
display: none;
}
--card-min-width: 291px;
--list-columns-number: 6;
.projects-group {
grid-column: 1/-1;
@ -819,43 +545,9 @@ li.module-hover {
}
.card {
background-color: $color-white;
color: $color-volcano;
display: flex;
flex-wrap: wrap;
grid-row: span 4;
height: 100%;
padding: .5em 1em;
position: relative;
width: 100%;
.checkbox-cell {
align-items: center;
display: flex;
height: 2em;
justify-content: center;
left: .5em;
position: absolute;
top: .5em;
width: 2em;
}
.user-cell {
.global-avatar-container {
height: 28px;
margin-right: .25em;
width: 28px;
}
.value {
display: flex;
flex-wrap: wrap;
}
}
.manage-users-link {
.project-users-link {
color: $color-silver-chalice;
display: inline;
&:hover {
text-decoration: none;
@ -971,48 +663,46 @@ li.module-hover {
display: none;
}
.more-users {
align-items: center;
background: $color-volcano;
border-radius: 50%;
color: $color-white;
cursor: pointer;
display: flex;
height: 2em;
justify-content: center;
margin-right: .25em;
width: 2em;
&:hover {
&.user-cell {
a:hover {
text-decoration: none;
}
}
.new-user {
align-items: center;
background: $color-concrete;
border-radius: 50%;
cursor: pointer;
display: flex;
height: 2em;
justify-content: center;
width: 2em;
.value {
display: flex;
flex-wrap: wrap;
}
.global-avatar-container {
height: 28px;
margin-right: .25em;
width: 28px;
}
.more-users {
align-items: center;
background: $color-volcano;
border-radius: 50%;
color: $color-white;
cursor: pointer;
display: flex;
height: 2em;
justify-content: center;
margin-right: .25em;
width: 2em;
}
.new-user {
background: $color-concrete;
text-align: center;
}
}
}
}
}
&.list {
grid-auto-flow: dense;
grid-auto-rows: 2em 1px;
grid-row-gap: .5em;
grid-template-columns: max-content repeat(3, minmax(100px, auto)) max-content max-content;
margin: 40px 0 0 6px;
.no-results-container {
grid-row: 12;
grid-column: 1 / -1;
}
grid-template-columns: max-content repeat(calc(var(--list-columns-number) - 3), minmax(100px, auto)) max-content max-content;
.projects-group {
display: contents;
@ -1031,17 +721,6 @@ li.module-hover {
}
.card {
display: contents;
// Border element
&:after {
background: $color-concrete;
content: "";
display: inline-block;
grid-column: 1/-1;
height: 1px;
}
.data-row {
font-weight: normal;
line-height: 1.25em;
@ -1061,10 +740,6 @@ li.module-hover {
font-weight: normal;
}
.checkbox-cell {
position: initial;
}
&.folder-card {
text-align: left;
@ -1076,6 +751,7 @@ li.module-hover {
.name {
grid-column: 5 span;
line-height: 3em;
&:before {
@include font-awesome;
@ -1094,12 +770,9 @@ li.module-hover {
display: none;
}
.checkbox-cell {
grid-column: 1;
}
.project-name-cell {
grid-column: 2;
height: 100%;
margin: 0;
a:hover {
@ -1120,39 +793,13 @@ li.module-hover {
}
.actions-cell {
align-items: center;
display: flex;
grid-column: 6;
position: initial;
}
}
}
.table-header {
display: contents;
&::after {
content: "";
grid-column: 1/-1;
}
.table-header-cell {
align-items: center;
border: 1px solid $color-white;
display: flex;
height: 3em;
margin-left: -.5em;
padding: 0 .5em;
width: calc(100% + 1em);
&.select-all-checkboxes {
justify-content: center;
position: relative;
}
}
& > * {
background-color: $color-concrete;
}
}
}
}
}
@ -1179,9 +826,6 @@ li.module-hover {
grid-auto-rows: 2.5em;
.card.project-card {
background: $color-concrete;
box-shadow: none;
.data-row {
color: $color-silver-chalice;
@ -1200,36 +844,15 @@ li.module-hover {
}
&.list {
grid-auto-rows: 2em 1px;
grid-template-columns: max-content repeat(5, minmax(100px, auto)) max-content;
--list-columns-number: 7;
grid-auto-rows: 3em 1px;
grid-template-columns: max-content repeat(calc(var(--list-columns-number) - 2), minmax(100px, auto)) max-content;
.card::after {
background: $color-white;
}
.card.folder-card {
.name {
grid-column: 6 span;
}
}
.card.project-card {
.checkbox-cell,
.project-name-cell,
.actions-cell,
.data-row {
align-items: center;
background: $color-concrete;
color: $color-volcano;
display: flex;
height: 3em;
margin-left: -.5em;
padding-left: .5em;
width: calc(100% + 1em);
}
.checkbox-cell {
padding-left: 0;
.card {
&.folder-card {
.name {
grid-column: 6 span;
}
}
.archived-date-cell {

View file

@ -368,6 +368,10 @@ label {
.file-name {
margin-left: 15px;
}
.image-icon.report {
display: none;
}
}
img {
@ -464,6 +468,10 @@ label {
margin-left: 5px;
white-space: nowrap;
}
.image-icon.report {
display: none;
}
}
&:hover > .report-element-header .file-name {

View file

@ -97,6 +97,14 @@ div.print-report {
img {
max-width: 100%;
}
.image-icon.preview {
display: none;
}
.image-icon.report {
display: initial !important;
}
}
.report-step-table-element {
@ -199,6 +207,14 @@ div.print-report {
}
}
.image-icon.preview {
display: none;
}
.image-icon.report {
display: initial !important;
}
img {
max-width: 100%;
}

View file

@ -31,66 +31,70 @@
#repository-toolbar {
align-items: center;
background-color: $color-white;
border: 0;
display: flex;
left: var(--repository-sidebar-margin);
padding: 0 2em;
position: fixed;
transition: .4s $timing-function-sharp;
width: calc(100% - var(--repository-sidebar-margin));
z-index: 100;
.toolbar-delimiter {
border-bottom: $border-tertiary;
bottom: 0;
position: absolute;
width: calc(100% - 4em);
}
.repository-share-icon {
flex-shrink: 0;
margin-top: -2px;
.fas-custom {
margin-left: 5px;
}
}
.dropdown-menu {
@include font-button;
}
.repository-title-name {
@include font-h2;
.title-row {
align-items: center;
background-color: $color-white;
border: 0;
display: flex;
flex-direction: column;
flex-grow: 1;
line-height: 32px;
margin-right: 20px;
overflow: hidden;
padding-left: 5px;
text-overflow: ellipsis;
white-space: nowrap;
left: var(--repository-sidebar-margin);
margin-left: 0em;
padding: 0 2em;
position: fixed;
transition: .4s $timing-function-sharp;
width: calc(100% - var(--repository-sidebar-margin));
z-index: 100;
.name-container {
align-items: center;
display: inline-flex;
height: 32px;
margin: 0;
.toolbar-delimiter {
border-bottom: $border-tertiary;
bottom: 0;
margin-left: -2em;
position: absolute;
width: 100%;
}
.read-only-name {
display: inline-block;
line-height: 32px;
max-width: calc(100% - 30px);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
.repository-share-icon {
flex-shrink: 0;
margin-top: -2px;
.fas-custom {
margin-left: 5px;
}
}
.inline-editing-container {
width: 100%;
.dropdown-menu {
@include font-button;
}
.repository-title-name {
@include font-h2;
display: flex;
flex-direction: column;
flex-grow: 1;
line-height: 32px;
margin-right: 20px;
overflow: hidden;
padding-left: 5px;
text-overflow: ellipsis;
white-space: nowrap;
.name-container {
align-items: center;
display: inline-flex;
height: 32px;
margin: 0;
.read-only-name {
display: inline-block;
line-height: 32px;
max-width: calc(100% - 30px);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.inline-editing-container {
width: 100%;
}
}
}
}

View file

@ -0,0 +1,178 @@
// scss-lint:disable SelectorDepth
// scss-lint:disable NestingDepth
// scss-lint:disable SelectorFormat
.cards-wrapper {
--card-min-width: 200px;
--list-columns-number: 5;
align-items: center;
display: grid;
grid-auto-rows: 2em;
grid-column-gap: 1em;
grid-row-gap: 1em;
grid-template-columns: repeat(auto-fill, minmax(var(--card-min-width), 1fr));
width: 100%;
.table-header {
display: none;
}
&.no-results {
.no-results-container {
grid-column: 1 / -1;
grid-row: 8;
}
.no-results-img {
display: block;
margin: auto;
max-height: 230px;
}
.no-results-title {
@include font-h1;
margin-bottom: .25em;
margin-top: 1.25em;
text-align: center;
}
.no-results-description {
@include font-main;
color: $color-silver-chalice;
text-align: center;
}
}
.card {
background-color: $color-white;
color: $color-volcano;
display: flex;
flex-wrap: wrap;
grid-row: span 4;
height: 100%;
padding: .5em 1em;
position: relative;
width: 100%;
.checkbox-cell {
align-items: center;
display: flex;
height: 2em;
justify-content: center;
left: .5em;
position: absolute;
top: .5em;
width: 2em;
}
}
&:not(.list) {
[list-render="true"] {
display: none !important;
}
}
&.list {
grid-auto-flow: dense;
grid-auto-rows: 3em 1px;
grid-column-gap: 0;
grid-row-gap: 0;
grid-template-columns: repeat(var(--list-columns-number), minmax(100px, auto));
[cards-render="true"] {
display: none !important;
}
.no-results-container {
grid-column: 1 / -1;
grid-row: 12;
}
.card {
display: contents;
// Border element
&::after {
background: $color-concrete;
content: "";
display: inline-block;
grid-column: 1/-1;
height: 1px;
}
&:hover {
.table-cell {
background: $color-concrete;
}
}
.table-cell {
height: 100%;
padding: 0 .5em;
width: 100%;
}
.checkbox-cell {
grid-column: 1;
position: initial;
}
}
.table-header {
display: contents;
&::after {
content: "";
grid-column: 1/-1;
}
.table-header-cell {
align-items: center;
border: 1px solid $color-white;
display: flex;
height: 3em;
padding: 0 .5em;
position: sticky;
position: -webkit-sticky;
top: calc(var(--content-header-size) + var(--navbar-height));
z-index: 2;
&.select-all-checkboxes {
justify-content: center;
}
}
& > * {
background-color: $color-concrete;
}
}
}
}
.content-pane.archived {
.cards-wrapper {
.card {
background: $color-concrete;
box-shadow: none !important;
}
&.list {
.card {
&::after {
background: $color-white;
}
.table-cell {
background: $color-concrete;
color: $color-volcano;
}
&:hover {
.table-cell {
background: $color-alto;
}
}
}
}
}
}

View file

@ -1,4 +1,8 @@
// scss-lint:disable SelectorDepth SelectorFormat
// scss-lint:disable NestingDepth QualifyingElement
.content-pane {
--content-header-size: 9.5em;
background-color: $color-white;
margin: 20px 0;
padding: 25px 20px;
@ -9,50 +13,141 @@
}
.content-header {
align-items: center;
border-bottom: $border-tertiary;
display: flex;
height: 5em;
h1 {
flex-grow: 1;
margin: 0;
&.sticky-header {
background: $color-white;
position: sticky;
position: -webkit-sticky;
top: var(--navbar-height);
z-index: 10;
}
.view-switch {
margin-left: auto;
.title-row {
align-items: center;
background: $color-white;
border-bottom: $border-tertiary;
display: flex;
height: 4em;
margin-left: -2em;
padding: 0 2em;
width: calc(100% + 4em);
.caret {
margin: 8px 0 8px 8px;
h1 {
flex-grow: 1;
margin: 0;
}
&.open {
.header-actions {
align-items: center;
display: flex;
margin-left: auto;
}
.view-switch {
margin-left: auto;
.caret {
transform: rotateX(180deg)
margin: 8px 0 8px 8px;
}
&.open {
.caret {
transform: rotateX(180deg);
}
}
.dropdown-menu {
@include font-button;
min-width: auto;
.divider-label {
@include font-small;
color: $color-silver-chalice;
padding: .25em 1em;
}
li {
cursor: pointer;
padding: 1em;
white-space: nowrap;
.button-icon {
margin-right: .5em;
}
&:hover {
background: $color-concrete;
}
}
}
.cards-switch {
&.active::after {
@include font-awesome;
content: "\f00c";
position: absolute;
right: 1em;
}
}
}
.dropdown-menu {
@include font-button;
min-width: auto;
.sort-menu {
display: inline-block;
li {
cursor: pointer;
padding: 1em;
white-space: nowrap;
#sortMenu {
background: $color-white;
.button-icon {
margin-right: .5em;
&:focus,
&:active {
box-shadow: none;
}
&:hover {
background: $color-concrete;
}
}
.dropdown-menu {
@include font-button;
min-width: 150px;
padding: .5em 0;
a {
border-radius: unset;
cursor: pointer;
padding: .5em 1em;
text-align: left;
&:hover {
background: $color-concrete;
}
&.selected::after {
@include font-awesome;
content: $font-fas-check;
margin-left: auto;
position: absolute;
right: 1em;
}
}
}
}
}
.toolbar-row {
background: $color-white;
border-bottom: $border-tertiary;
margin-left: -2em;
padding: 1em 0;
padding-left: 2em;
width: calc(100% + 4em);
}
}
.content-body {
&[data-toolbar-visible="false"] {
--content-header-size: 5em;
.toolbar-row {
display: none;
}
}
}

View file

@ -0,0 +1,124 @@
// scss-lint:disable NestingDepth SelectorDepth
.filter-container {
position: initial;
.filter-dropdown {
padding: 0;
width: 230px;
.header {
align-items: center;
border-bottom: $border-default;
display: flex;
height: 2.75em;
margin-bottom: 1em;
padding: 0 .3em 0 1em;
.title {
@include font-h2;
flex-grow: 1;
user-select: none;
}
}
.select-block {
display: inline-block;
padding: 0 1em 1em;
position: relative;
width: 100%;
label {
@include font-small;
font-weight: bold;
margin-bottom: .3em;
user-select: none;
}
}
.text-search-filter {
&.open {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
}
.recent-searches {
border-top-left-radius: 0;
border-top-right-radius: 0;
left: auto;
margin-top: -1px;
top: auto;
width: calc(100% - 2em);
label {
@include font-small;
color: $color-silver-chalice;
font-weight: bold;
margin-bottom: .3em;
padding: 0 1.5em;
user-select: none;
}
.projects-search-keyword {
@include font-button;
padding: .5em 1.25em;
.keyword-text {
margin-left: .25em;
}
}
}
.footer {
align-items: center;
border-top: $border-default;
display: flex;
height: 4.25em;
justify-content: center;
padding: 0 1em;
position: relative;
width: 100%;
}
}
&.filters-applied .filter-button::after {
@include red-dot;
right: .35em;
top: .3em;
}
.connect-line {
background: $input-border;
height: 10px;
margin: 5px auto;
width: 2px;
}
@media (min-width: 768px) {
.filter-dropdown {
width: 460px;
}
.footer:not(.center) {
.btn:last-child {
margin-left: auto;
}
}
.datetime-picker-container {
display: inline-block;
width: 46%;
}
.connect-line {
display: inline-block;
margin: 0 11px;
position: relative;
top: 2px;
transform: rotate(90deg);
}
}
}

View file

@ -632,7 +632,6 @@ ul.double-line > li {
}
#project-show,
#project-show-archive,
#module-archive,
#result-archive {
.panel-default {
@ -657,84 +656,6 @@ ul.double-line > li {
}
}
.panel-project {
box-shadow: 0 3px 6px $color-alto;
color: $color-silver-chalice;
&:not(.selected) .panel-heading .project-card-selector,
&:not(.selected) .panel-heading .dropdown {
display: none;
}
&:not(.selected):hover .project-card-selector,
&:not(.selected):hover .dropdown {
display: block;
}
.nav .btn-link {
padding: 10px 5px;
}
&.selected {
border-color: $brand-primary;
box-shadow: 0 3px 10px $brand-primary;
}
&.archived {
&,
.panel-heading,
.panel-body,
.panel-footer-scinote {
background-color: $color-concrete;
background-image: none;
}
}
.panel-heading {
background-color: $color-white;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
color: $color-silver-chalice;
.fas {
margin-right: 10px;
}
}
.panel-title {
color: $brand-primary;
.fas {
color: $color-silver-chalice;
}
}
.panel-body {
padding: 10px 15px;
.row {
padding: 2px;
}
}
.panel-footer {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
.nav {
padding: 0 10px;
}
.nav-tabs {
border-bottom: 0;
}
}
.tab-pane {
color: $color-emperor;
}
}
.panel-archive {
.panel-heading {
background-color: darken($color-gainsboro, 5%);

View file

Before

Width:  |  Height:  |  Size: 474 KiB

After

Width:  |  Height:  |  Size: 474 KiB

View file

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

View file

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

View file

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 187 KiB

File diff suppressed because it is too large Load diff

View file

@ -595,7 +595,7 @@
"step": {
"id": 4382,
"name": "Prepare antibiotic dilutions",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>Prepare antibiotic dilutions following steps bellow.</p></body></html>",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>Prepare antibiotic dilutions following steps below.</p></body></html>",
"position": 0,
"completed": false,
"completed_on": null,
@ -808,7 +808,7 @@
"step": {
"id": 4370,
"name": "Prepare agar plates",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>Prepare agar plates following guidelines bellow.</p></body></html>",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>Prepare agar plates following guidelines below.</p></body></html>",
"position": 3,
"completed": false,
"completed_on": null,
@ -1401,7 +1401,7 @@
"step": {
"id": 4362,
"name": "Isolate preparation",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>Follow steps bellow for isolate preparation.</p></body></html>",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>Follow steps below for isolate preparation.</p></body></html>",
"position": 1,
"completed": false,
"completed_on": null,

View file

@ -700,7 +700,7 @@
"step": {
"id": 4414,
"name": "Preparation",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>Guidelines for samples preparation bellow. </p></body></html>",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>Guidelines for samples preparation below. </p></body></html>",
"position": 0,
"completed": false,
"completed_on": null,

View file

@ -88,7 +88,7 @@
"step": {
"id": 4533,
"name": "Examine the plates",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>Follow the guidelines bellow. </p></body></html>",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>Follow the guidelines below. </p></body></html>",
"position": 0,
"completed": false,
"completed_on": null,
@ -863,7 +863,7 @@
"step": {
"id": 5495,
"name": "Slide test",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>Follow the guidelines bellow. </p></body></html>",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>Follow the guidelines below. </p></body></html>",
"position": 0,
"completed": false,
"completed_on": null,
@ -1020,7 +1020,7 @@
"step": {
"id": 5492,
"name": "Oxidase test",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>Follow the guidelines bellow: </p></body></html>",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>Follow the guidelines below: </p></body></html>",
"position": 0,
"completed": false,
"completed_on": null,
@ -1604,7 +1604,7 @@
"step": {
"id": 5475,
"name": "Reagents",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body>\n<p>Reagents bellow can be made or purchased.<br><strong><br>Crystal Violet Staining Reagent:<br></strong><span style=\"text-decoration: underline;\"><br>Solution A for crystal violet staining reagent</span></p>\n<ul>\n<li>Crystal violet (certified 90% dye content), 2g </li>\n<li>Ethanol, 95% (vol/vol), 20 mL</li>\n</ul>\n<span style=\"text-decoration: underline;\">Solution B for crystal violet staining reagent<br></span>\n<ul>\n<li>Ammonium oxalate, 0.8 g</li>\n<li>Distilled water, 80 mL</li>\n</ul>\nMix A and B to obtain crystal violet staining reagent. Store for<strong> 24 h</strong> and filter through paper prior to use.<br><br><strong>Gram's Iodine:<br></strong><br>\n<ul>\n<li>Iodine, 1.0 g</li>\n<li>Potassium iodide, 2.0 g</li>\n<li>Distilled water, 300 mL</li>\n</ul>\nGrind the iodine and potassium iodide in a mortar and add water slowly with continuous grinding until the iodine is dissolved. Store in amber bottles.<br><br><strong>Safranin:<br><br></strong><span style=\"text-decoration-line: underline;\">Stock solution:</span><br>\n<ul>\n<li>2.5g Safranin O</li>\n<li>100 ml 95% Ethanol</li>\n</ul>\n<p><span style=\"text-decoration: underline;\">Working Solution:</span></p>\n<ul>\n<li>10 mL Stock Solution </li>\n<li>90 mL Distilled water</li>\n</ul>\n</body></html>",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body>\n<p>Reagents below can be made or purchased.<br><strong><br>Crystal Violet Staining Reagent:<br></strong><span style=\"text-decoration: underline;\"><br>Solution A for crystal violet staining reagent</span></p>\n<ul>\n<li>Crystal violet (certified 90% dye content), 2g </li>\n<li>Ethanol, 95% (vol/vol), 20 mL</li>\n</ul>\n<span style=\"text-decoration: underline;\">Solution B for crystal violet staining reagent<br></span>\n<ul>\n<li>Ammonium oxalate, 0.8 g</li>\n<li>Distilled water, 80 mL</li>\n</ul>\nMix A and B to obtain crystal violet staining reagent. Store for<strong> 24 h</strong> and filter through paper prior to use.<br><br><strong>Gram's Iodine:<br></strong><br>\n<ul>\n<li>Iodine, 1.0 g</li>\n<li>Potassium iodide, 2.0 g</li>\n<li>Distilled water, 300 mL</li>\n</ul>\nGrind the iodine and potassium iodide in a mortar and add water slowly with continuous grinding until the iodine is dissolved. Store in amber bottles.<br><br><strong>Safranin:<br><br></strong><span style=\"text-decoration-line: underline;\">Stock solution:</span><br>\n<ul>\n<li>2.5g Safranin O</li>\n<li>100 ml 95% Ethanol</li>\n</ul>\n<p><span style=\"text-decoration: underline;\">Working Solution:</span></p>\n<ul>\n<li>10 mL Stock Solution </li>\n<li>90 mL Distilled water</li>\n</ul>\n</body></html>",
"position": 0,
"completed": false,
"completed_on": null,

View file

@ -1210,7 +1210,7 @@
"step": {
"id": 5154,
"name": "Elution and collection of samples",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>Follow elution and collection guidelines bellow.</p></body></html>",
"description": "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">\n<html><body><p>Follow elution and collection guidelines below.</p></body></html>",
"position": 1,
"completed": false,
"completed_on": null,

View file

@ -0,0 +1,15 @@
# frozen_string_literal: true
module ActiveStorage
module Blobs
class RedirectController < CustomBaseController
include ActiveStorage::SetBlob
include ActiveStorage::CheckBlobPermissions
def show
expires_in ActiveStorage.service_urls_expire_in
redirect_to @blob.url(disposition: params[:disposition])
end
end
end
end

View file

@ -1,13 +0,0 @@
# frozen_string_literal: true
module ActiveStorage
class BlobsController < CustomBaseController
include ActiveStorage::SetBlob
include ActiveStorage::CheckBlobPermissions
def show
expires_in ActiveStorage.service_urls_expire_in
redirect_to @blob.service_url(disposition: params[:disposition])
end
end
end

View file

@ -8,5 +8,11 @@ module ActiveStorage
prepend_before_action :authenticate_request!, if: -> { request.headers['Authorization'].present? }
skip_before_action :authenticate_user!, if: -> { current_user.present? }
private
def stream(_blob)
raise NotImplementedError
end
end
end

View file

@ -5,7 +5,7 @@ module ActiveStorage
before_action :check_file_size, only: :create
def create
blob = ActiveStorage::Blob.create_before_direct_upload!(blob_args)
blob = ActiveStorage::Blob.create_before_direct_upload!(**blob_args)
render json: direct_upload_json(blob)
end
@ -13,7 +13,7 @@ module ActiveStorage
def blob_args
args = params.require(:blob)
.permit(:filename, :byte_size, :checksum, :content_type, :metadata)
.permit(:filename, :byte_size, :checksum, :content_type, metadata: {})
.to_h
.symbolize_keys
args[:content_type] = 'application/octet-stream' if args[:content_type].blank?

View file

@ -0,0 +1,44 @@
# frozen_string_literal: true
module ActiveStorage
module Representations
class RedirectController < CustomBaseController
include ActiveStorage::SetBlob
include ActiveStorage::CheckBlobPermissions
def show
if @blob.attachments.take.record_type == 'Asset'
return render plain: '', status: :accepted unless preview_ready?
end
expires_in ActiveStorage.service_urls_expire_in
redirect_to @blob.representation(params[:variation_key]).processed.url(disposition: params[:disposition])
end
private
def preview_ready?
processing = @blob.attachments.take.record.file_processing
return false if processing
preview_exists =
if @blob.variable?
@blob.service.exist?(@blob.representation(params['variation_key']).key)
else
@blob.preview(params['variation_key']).image.attached?
end
return true if preview_exists
unless processing
ActiveStorage::PreviewJob.perform_later(@blob.id)
ActiveRecord::Base.no_touching do
@blob.attachments.take.record.update(file_processing: true)
end
end
false
end
end
end
end

View file

@ -1,42 +0,0 @@
# frozen_string_literal: true
module ActiveStorage
class RepresentationsController < CustomBaseController
include ActiveStorage::SetBlob
include ActiveStorage::CheckBlobPermissions
def show
if @blob.attachments.take.record_type == 'Asset'
return render plain: '', status: :accepted unless preview_ready?
end
expires_in ActiveStorage.service_urls_expire_in
redirect_to @blob.representation(params[:variation_key]).processed.service_url(disposition: params[:disposition])
end
private
def preview_ready?
processing = @blob.attachments.take.record.file_processing
return false if processing
preview_exists =
if @blob.variable?
@blob.service.exist?(@blob.representation(params['variation_key']).key)
else
@blob.preview(params['variation_key']).image.attached?
end
return true if preview_exists
unless processing
ActiveStorage::PreviewJob.perform_later(@blob.id)
ActiveRecord::Base.no_touching do
@blob.attachments.take.record.update(file_processing: true)
end
end
false
end
end
end

View file

@ -25,7 +25,7 @@ module Api
if @form_multipart_upload
asset = @step.assets.new(asset_params.merge({ team_id: @team.id }))
else
blob = ActiveStorage::Blob.create_after_upload!(
blob = ActiveStorage::Blob.create_and_upload!(
io: StringIO.new(Base64.decode64(asset_params[:file_data])),
filename: asset_params[:file_name],
content_type: asset_params[:file_type]

View file

@ -129,7 +129,7 @@ module Api
end
def create_blob_from_params
blob = ActiveStorage::Blob.create_after_upload!(
blob = ActiveStorage::Blob.create_and_upload!(
io: StringIO.new(Base64.decode64(result_file_params[:file_data])),
filename: result_file_params[:file_name],
content_type: result_file_params[:file_type]

View file

@ -11,7 +11,10 @@ module ActiveStorage
private
def check_read_permissions
case @blob.attachments.first.record_type
attachment = @blob.attachments.take
return render_404 if attachment.blank?
case attachment.record_type
when 'Asset'
check_asset_read_permissions
when 'TinyMceAsset'

View file

@ -7,9 +7,9 @@ class ExperimentsController < ApplicationController
include ApplicationHelper
include Rails.application.routes.url_helpers
before_action :load_project, only: %i(new create)
before_action :load_experiment, except: %i(new create)
before_action :check_view_permissions, except: %i(edit archive clone move new create)
before_action :load_project, only: %i(new create archive_group restore_group)
before_action :load_experiment, except: %i(new create archive_group restore_group)
before_action :check_view_permissions, except: %i(edit archive clone move new create archive_group restore_group)
before_action :check_create_permissions, only: %i(new create)
before_action :check_manage_permissions, only: %i(edit)
before_action :check_archive_permissions, only: :archive
@ -39,7 +39,7 @@ class ExperimentsController < ApplicationController
@experiment.project = @project
if @experiment.save
experiment_annotation_notification
log_activity(:create_experiment)
log_activity(:create_experiment, @experiment)
flash[:success] = t('experiments.create.success_flash',
experiment: @experiment.name)
respond_to do |format|
@ -56,10 +56,16 @@ class ExperimentsController < ApplicationController
end
end
def show
render json: {
html: render_to_string(partial: 'experiments/details_modal.html.erb')
}
end
def canvas
redirect_to module_archive_experiment_path(@experiment) if @experiment.archived_branch?
@project = @experiment.project
@active_modules = @experiment.my_modules.active.includes(:tags, :inputs, :outputs)
@active_modules = @experiment.my_modules.active.order(:name).includes(:tags, :inputs, :outputs)
current_team_switch(@project.team)
end
@ -98,15 +104,14 @@ class ExperimentsController < ApplicationController
else
:edit_experiment
end
log_activity(activity_type)
log_activity(activity_type, @experiment)
respond_to do |format|
format.json do
render json: {}, status: :ok
end
format.html do
flash[:success] = t('experiments.update.success_flash',
experiment: @experiment.name)
flash[:success] = t('experiments.update.success_flash', experiment: @experiment.name)
redirect_to project_path(@experiment.project)
end
end
@ -124,11 +129,9 @@ class ExperimentsController < ApplicationController
end
def archive
@experiment.archived = true
@experiment.archived_by = current_user
@experiment.archived_on = DateTime.now
@experiment.archive(current_user)
if @experiment.save
log_activity(:archive_experiment)
log_activity(:archive_experiment, @experiment)
flash[:success] = t('experiments.archive.success_flash',
experiment: @experiment.name)
@ -139,6 +142,51 @@ class ExperimentsController < ApplicationController
end
end
def archive_group
experiments = @project.experiments.active.where(id: params[:experiments_ids])
counter = 0
experiments.each do |experiment|
next unless can_archive_experiment?(experiment)
experiment.transaction do
experiment.archived_on = DateTime.now
experiment.archive!(current_user)
log_activity(:archive_experiment, experiment)
counter += 1
rescue StandardError => e
Rails.logger.error e.message
raise ActiveRecord::Rollback
end
end
if counter.positive?
render json: { message: t('experiments.archive_group.success_flash', number: counter) }
else
render json: { message: t('experiments.archive_group.error_flash') }, status: :unprocessable_entity
end
end
def restore_group
experiments = @project.experiments.archived.where(id: params[:experiments_ids])
counter = 0
experiments.each do |experiment|
next unless can_restore_experiment?(experiment)
experiment.transaction do
experiment.restore!(current_user)
log_activity(:restore_experiment, experiment)
counter += 1
rescue StandardError => e
Rails.logger.error e.message
raise ActiveRecord::Rollback
end
end
if counter.positive?
render json: { message: t('experiments.restore_group.success_flash', number: counter) }
else
render json: { message: t('experiments.restore_group.error_flash') }, status: :unprocessable_entity
end
end
# GET: clone_modal_experiment_path(id)
def clone_modal
@projects = @experiment.projects_with_role_above_user(current_user)
@ -287,6 +335,7 @@ class ExperimentsController < ApplicationController
def set_inline_name_editing
return unless can_manage_experiment?(@experiment)
@inline_editable_title_config = {
name: 'title',
params_group: 'experiment',
@ -311,13 +360,13 @@ class ExperimentsController < ApplicationController
)
end
def log_activity(type_of)
def log_activity(type_of, experiment)
Activities::CreateActivityService
.call(activity_type: type_of,
owner: current_user,
team: @experiment.project.team,
project: @experiment.project,
subject: @experiment,
message_items: { experiment: @experiment.id })
team: experiment.project.team,
project: experiment.project,
subject: experiment,
message_items: { experiment: experiment.id })
end
end

View file

@ -30,15 +30,15 @@ class ExternalProtocolsController < ApplicationController
.split('/').map(&:to_sym))
api_client = "ProtocolImporters::#{endpoint_name}::ApiClient".constantize.new
html_preview = api_client.protocol_html_preview(show_params[:protocol_id])
base_uri = URI.parse(html_preview.request.last_uri.to_s)
html_preview_request = api_client.protocol_html_preview(show_params[:protocol_id])
base_uri = URI.parse(html_preview_request.request.last_uri.to_s)
base_uri = "#{base_uri.scheme}://#{base_uri.host}"
render json: {
protocol_source: show_params[:protocol_source],
protocol_id: show_params[:protocol_id],
base_uri: base_uri,
html: html_preview
html: html_preview_request.body
}
rescue StandardError => e
render json: {

View file

@ -96,6 +96,35 @@ class ProjectFoldersController < ApplicationController
end
end
def destroy_modal
render json: {
html: render_to_string(partial: 'projects/index/modals/project_folder_delete.html.erb',
locals: { project_folders_ids: params[:project_folders_ids] })
}
end
def destroy
project_folders = current_team.project_folders.where(id: params[:project_folders_ids])
counter = 0
project_folders.each do |folder|
next if folder.projects.exists? || folder.project_folders.exists? || !can_update_team?(current_team)
folder.transaction do
log_activity(:delete_project_folder, folder, project_folder: folder.id)
folder.destroy!
counter += 1
rescue StandardError => e
Rails.logger.error e.message
raise ActiveRecord::Rollback
end
end
if counter.positive?
render json: { message: t('projects.delete_folders.success_flash', number: counter) }
else
render json: { message: t('projects.delete_folders.error_flash') }, status: :unprocessable_entity
end
end
private
def load_project_folder

View file

@ -5,18 +5,19 @@ class ProjectsController < ApplicationController
include TeamsHelper
include InputSanitizeHelper
include ProjectsHelper
include ExperimentsHelper
attr_reader :current_folder
helper_method :current_folder
before_action :switch_team_with_param, only: :index
before_action :load_vars, only: %i(show edit update notifications experiment_archive sidebar)
before_action :load_current_folder, only: %i(index cards new)
before_action :check_view_permissions, only: %i(show notifications experiment_archive sidebar)
before_action :load_vars, only: %i(show edit update notifications sidebar experiments_cards)
before_action :load_current_folder, only: %i(index cards new show)
before_action :check_view_permissions, only: %i(show notifications sidebar experiments_cards)
before_action :check_create_permissions, only: %i(new create)
before_action :check_manage_permissions, only: :edit
before_action :set_inline_name_editing, only: %i(show)
before_action :load_exp_sort_var, only: %i(show experiment_archive)
before_action :load_exp_sort_var, only: %i(show)
before_action :reset_invalid_view_state, only: %i(index cards)
layout 'fluid'
@ -30,6 +31,7 @@ class ProjectsController < ApplicationController
def cards
overview_service = ProjectsOverviewService.new(current_team, current_user, current_folder, params)
title = params[:view_mode] == 'archived' ? t('projects.index.head_title_archived') : t('projects.index.head_title')
if filters_included?
render json: {
@ -43,6 +45,7 @@ class ProjectsController < ApplicationController
render json: {
projects_cards_url: current_folder ? project_folder_cards_url(current_folder) : cards_projects_url,
breadcrumbs_html: current_folder ? render_to_string(partial: 'projects/index/breadcrumbs.html.erb') : '',
title: current_folder ? current_folder&.name : title,
toolbar_html: render_to_string(partial: 'projects/index/toolbar.html.erb'),
cards_html: render_to_string(
partial: 'projects/index/team_projects.html.erb',
@ -53,15 +56,15 @@ class ProjectsController < ApplicationController
end
def sidebar
respond_to do |format|
format.json do
render json: {
html: render_to_string(
partial: 'shared/sidebar/experiments.html.erb', locals: { project: @project }
)
@current_sort = @project.current_view_state(current_user).state.dig('experiments', params[:view_mode], 'sort')
render json: {
html: render_to_string(
partial: 'shared/sidebar/experiments.html.erb', locals: {
project: @project,
view_mode: experiments_view_mode(@project)
}
end
end
)
}
end
def new
@ -253,9 +256,22 @@ class ProjectsController < ApplicationController
end
def show
redirect_to action: :experiment_archive if @project.archived?
# This is the "info" view
current_team_switch(@project.team)
view_state = @project.current_view_state(current_user)
@current_sort = view_state.state.dig('experiments', experiments_view_mode(@project), 'sort') || 'atoz'
end
def experiments_cards
overview_service = ExperimentsOverviewService.new(@project, current_user, params)
render json: {
cards_html: render_to_string(
partial: 'projects/show/experiments_list.html.erb',
locals: { cards: overview_service.experiments,
filters_included: filters_included? }
)
}
end
def notifications
@ -274,10 +290,6 @@ class ProjectsController < ApplicationController
end
end
def experiment_archive
current_team_switch(@project.team)
end
def users_filter
users = current_team.users.search(false, params[:query]).map do |u|
{ value: u.id, label: sanitize_input(u.name), params: { avatar_url: avatar_path(u, :icon_small) } }
@ -301,6 +313,8 @@ class ProjectsController < ApplicationController
def load_current_folder
if current_team && params[:project_folder_id].present?
@current_folder = current_team.project_folders.find_by(id: params[:project_folder_id])
elsif @project&.project_folder
@current_folder = @project&.project_folder
end
end
@ -334,11 +348,11 @@ class ProjectsController < ApplicationController
@project.save
end
@current_sort = @project.experiments_order || 'new'
@current_sort = 'new' if @current_sort.include?('arch') && action_name != 'experiment_archive'
end
def filters_included?
%i(search created_on_from created_on_to members archived_on_from archived_on_to folders_search)
%i(search created_on_from created_on_to updated_on_from updated_on_to members
archived_on_from archived_on_to folders_search)
.any? { |param_name| params.dig(param_name).present? }
end

View file

@ -26,7 +26,9 @@ class RepositoriesController < ApplicationController
def index
respond_to do |format|
format.html do; end
format.html do
render 'empty_index' if Repository.accessible_by_teams(current_team).blank?
end
format.json do
render json: prepare_repositories_datatable(@repositories, current_team, params)
end

View file

@ -1,5 +1,6 @@
class SearchController < ApplicationController
include IconsHelper
include ProjectFoldersHelper
before_action :load_vars, only: :index
def index

View file

@ -73,6 +73,8 @@ class TeamRepositoriesController < ApplicationController
end
def teams_to_update
return [] if update_params[:permission_changes].blank?
teams_to_update = JSON.parse(update_params[:permission_changes]).keys.map(&:to_i).to_a &
update_params[:share_team_ids]&.map(&:to_i).to_a
wp = update_params[:write_permissions]&.map(&:to_i)

View file

@ -10,16 +10,12 @@ class UserProjectsController < ApplicationController
before_action :check_manage_permissions, only: %i(update destroy)
def index
@users = @project.user_projects
@user_projects = @project.user_projects
respond_to do |format|
format.json do
render json: {
html: render_to_string(
partial: 'index.html.erb'
),
project_id: @project.id,
counter: @project.users.count # Used for counter badge
html: render_to_string(partial: 'index.html.erb')
}
end
end
@ -33,7 +29,6 @@ class UserProjectsController < ApplicationController
respond_to do |format|
format.json do
render json: {
project: @project,
html_title: t('projects.index.modal_manage_users.modal_title', name: @project.name),
html_body: render_to_string(partial: 'index_edit.html.erb'),
html_footer: render_to_string(partial: 'index_edit_footer.html.erb')
@ -51,7 +46,7 @@ class UserProjectsController < ApplicationController
respond_to do |format|
format.json do
redirect_to project_users_edit_path(format: :json), turbolinks: false
redirect_to edit_project_users_path(format: :json), turbolinks: false
end
end
else
@ -77,7 +72,7 @@ class UserProjectsController < ApplicationController
respond_to do |format|
format.json do
redirect_to project_users_edit_path(format: :json), turbolinks: false
redirect_to edit_project_users_path(format: :json), turbolinks: false
end
end
else
@ -97,7 +92,7 @@ class UserProjectsController < ApplicationController
log_activity(:unassign_user_from_project)
respond_to do |format|
format.json do
redirect_to project_users_edit_path(format: :json),
redirect_to edit_project_users_path(format: :json),
turbolinks: false,
status: :see_other
end

View file

@ -25,7 +25,7 @@ class Users::SessionsController < Devise::SessionsController
def create
super
generate_demo_project
generate_templates_project
end
def two_factor_recovery
@ -80,7 +80,7 @@ class Users::SessionsController < Devise::SessionsController
session.delete(:otp_user_id)
sign_in(user)
generate_demo_project
generate_templates_project
flash[:notice] = t('devise.sessions.signed_in')
redirect_to stored_location_for(:user) || root_path
else
@ -100,7 +100,7 @@ class Users::SessionsController < Devise::SessionsController
session.delete(:otp_user_id)
if user.recover_2fa!(params[:recovery_code])
sign_in(user)
generate_demo_project
generate_templates_project
flash[:notice] = t('devise.sessions.signed_in')
redirect_to root_path
else
@ -130,17 +130,9 @@ class Users::SessionsController < Devise::SessionsController
end
end
def generate_demo_project
def generate_templates_project
# Schedule templates creation for user
TemplatesService.new.schedule_creation_for_user(current_user)
# Schedule demo project creation for user
current_user.created_teams.each do |team|
FirstTimeDataGenerator.delay(
queue: :new_demo_project,
priority: 10
).seed_demo_data_with_id(current_user.id, team.id)
end
rescue StandardError => e
Rails.logger.fatal("User ID #{current_user.id}: Error creating inital projects on sign_in: #{e.message}")
end

View file

@ -1,6 +1,12 @@
# frozen_string_literal: true
module ExperimentsHelper
def experiments_view_mode(project)
return 'archived' if project.archived?
params[:view_mode] == 'archived' ? 'archived' : 'active'
end
def grouped_by_prj(experiments)
ungrouped_experiments = experiments.joins(:project)
.select('projects.name as project_name,

View file

@ -30,7 +30,7 @@ module FileIconsHelper
end
# For showing next to file
def file_extension_icon(asset)
def file_extension_icon(asset, report = false)
file_ext = asset.file_name.split('.').last
if Constants::FILE_TEXT_FORMATS.include?(file_ext)
image_link = 'icon_small/docx_file.svg'
@ -46,7 +46,14 @@ module FileIconsHelper
image_link = Extends::FILE_ICON_MAPPINGS[file_ext] if Extends::FILE_ICON_MAPPINGS[file_ext]
if image_link
ActionController::Base.helpers.image_tag(image_link, class: 'image-icon')
if report
content_tag(:span) do
concat ActionController::Base.helpers.image_tag(image_link, class: 'image-icon preview')
concat wicked_pdf_image_tag(image_link, class: 'image-icon report')
end
else
ActionController::Base.helpers.image_tag(image_link, class: 'image-icon')
end
else
''
end
@ -100,8 +107,8 @@ module FileIconsHelper
end
end
def file_extension_icon_html(asset)
html = file_extension_icon(asset)
def file_extension_icon_html(asset, report = false)
html = file_extension_icon(asset, report)
if html.blank?
html = ActionController::Base.helpers.content_tag(
:i,

View file

@ -12,7 +12,7 @@ module GlobalActivitiesHelper
if value.is_a? String
value
elsif value['type'] == 'Time' # use saved date for printing
l(Time.at(value['value']), format: :full)
I18n.l(Time.zone.at(value['value']), format: :full)
else
no_links ? generate_name(value) : generate_link(value, activity)
end
@ -63,7 +63,7 @@ module GlobalActivitiesHelper
when Experiment
return current_value unless obj.navigable?
path = obj.archived? ? experiment_archive_project_path(obj.project) : canvas_experiment_path(obj)
path = obj.archived? ? project_path(obj.project, view_mode: :archived) : canvas_experiment_path(obj)
when MyModule
return current_value unless obj.navigable?

View file

@ -215,7 +215,7 @@ class Asset < ApplicationRecord
raise ArgumentError, 'Destination asset should be persisted first!' unless to_asset.persisted?
file.blob.open do |tmp_file|
to_blob = ActiveStorage::Blob.create_after_upload!(io: tmp_file, filename: blob.filename, metadata: blob.metadata)
to_blob = ActiveStorage::Blob.create_and_upload!(io: tmp_file, filename: blob.filename, metadata: blob.metadata)
to_asset.file.attach(to_blob)
end
to_asset.post_process_file(to_asset.team)

View file

@ -33,10 +33,6 @@ class Experiment < ApplicationRecord
validates :last_modified_by, presence: true
validates :uuid, uniqueness: { scope: :project },
unless: proc { |e| e.uuid.blank? }
with_options if: :archived do |experiment|
experiment.validates :archived_by, presence: true
experiment.validates :archived_on, presence: true
end
scope :is_archived, lambda { |is_archived|
if is_archived
@ -472,12 +468,17 @@ class Experiment < ApplicationRecord
def normalize_module_positions
# This method normalizes module positions so x-s and y-s
# are all positive
x_diff = my_modules.pluck(:x).min
y_diff = my_modules.pluck(:y).min
x_diff = my_modules.active.pluck(:x).min
y_diff = my_modules.active.pluck(:y).min
my_modules.each do |m|
m.update(x: m.x - x_diff, y: m.y - y_diff)
moving_direction = {
x: x_diff.positive? ? :asc : :desc,
y: y_diff.positive? ? :asc : :desc
}
my_modules.active.order(moving_direction).each do |m|
m.update!(x: m.x - x_diff, y: m.y - y_diff)
end
my_modules.reload
end
# Recalculate module groups in this project. Input is

View file

@ -2,6 +2,7 @@ class Project < ApplicationRecord
include ArchivableModel
include SearchableModel
include SearchableByNameModel
include ViewableModel
enum visibility: { hidden: 0, visible: 1 }
@ -150,6 +151,22 @@ class Project < ApplicationRecord
.distinct
end
def default_view_state
{
experiments: {
active: { sort: 'new' },
archived: { sort: 'new' }
}
}
end
def validate_view_state(view_state)
if %w(new old atoz ztoa).exclude?(view_state.state.dig('experiments', 'active', 'sort')) ||
%w(new old atoz ztoa archived_new archived_old).exclude?(view_state.state.dig('experiments', 'archived', 'sort'))
view_state.errors.add(:state, :wrong_state)
end
end
def last_activities(count = Constants::ACTIVITY_AND_NOTIF_SEARCH_LIMIT)
activities.order(created_at: :desc).first(count)
end

View file

@ -8,7 +8,7 @@ class ProjectFolder < ApplicationRecord
validates :name,
length: { minimum: Constants::NAME_MIN_LENGTH,
maximum: Constants::NAME_MAX_LENGTH },
uniqueness: { scope: %i(team_id parent_folder_id), case_sensitive: false }
uniqueness: { scope: %i(team_id parent_folder_id archived), case_sensitive: false }
validate :parent_folder_team, if: -> { parent_folder.present? }
validate :parent_folder_validation, if: -> { parent_folder.present? }

View file

@ -43,17 +43,4 @@ class Tag < ApplicationRecord
.offset((page - 1) * Constants::SEARCH_LIMIT)
end
end
def clone_to_project_or_return_existing(project)
tag = Tag.find_by(project: project, name: name, color: color)
return tag if tag
Tag.create(
name: name,
color: color,
created_by: created_by,
last_modified_by: last_modified_by,
project: project
)
end
end

View file

@ -43,8 +43,6 @@ class Team < ApplicationRecord
has_many :shared_repositories, through: :team_repositories, source: :repository
attr_accessor :without_templates
attr_accessor :without_intro_demo
after_create :generate_intro_demo
def default_view_state
{
@ -168,15 +166,4 @@ class Team < ApplicationRecord
return if without_templates
TemplatesService.new.delay(queue: :templates).update_team(self)
end
include FirstTimeDataGenerator
def generate_intro_demo
return if without_intro_demo
user = User.find(created_by_id)
if user.created_teams.order(:created_at).first == self
seed_demo_data(user, self)
end
end
end

View file

@ -194,7 +194,7 @@ class TinyMceAsset < ApplicationRecord
raise ArgumentError, 'Destination TinyMce asset should be persisted first!' unless to_asset.persisted?
image.blob.open do |tmp_file|
to_blob = ActiveStorage::Blob.create_after_upload!(io: tmp_file, filename: blob.filename, metadata: blob.metadata)
to_blob = ActiveStorage::Blob.create_and_upload!(io: tmp_file, filename: blob.filename, metadata: blob.metadata)
to_asset.image.attach(to_blob)
end
TinyMceAsset.update_estimated_size(to_asset.id)

View file

@ -62,8 +62,12 @@ Canaid::Permissions.register_for(Project) do
end
# experiment: create
can :create_experiments do |user, project|
user.is_user_or_higher_of_project?(project)
%i(create_experiments
manage_experiments)
.each do |perm|
can perm do |user, project|
user.is_user_or_higher_of_project?(project)
end
end
# project: create comment
@ -93,3 +97,10 @@ Canaid::Permissions.register_for(ProjectComment) do
user.is_owner_of_project?(project_comment.project))
end
end
Canaid::Permissions.register_for(ProjectFolder) do
# ProjectFolder: delete
can :delete_project_folder do |_, project_folder|
!project_folder.projects.exists? && !project_folder.project_folders.exists?
end
end

View file

@ -16,10 +16,10 @@ class ActivitiesService
subjects_with_children = load_subjects_children(filters[:subjects])
if subjects_with_children['Project']
query = query.where('project_id IN (?)', subjects_with_children['Project'])
subjects_with_children.except!('Project')
subjects_with_children = subjects_with_children.except('Project')
end
where_condition = subjects_with_children.map { '(subject_type = ? AND subject_id IN(?))' }.join(' OR ')
where_arguments = subjects_with_children.flatten
where_condition = subjects_with_children.to_h.map { '(subject_type = ? AND subject_id IN(?))' }.join(' OR ')
where_arguments = subjects_with_children.to_h.flatten
if subjects_with_children[:my_module]
where_condition = where_condition.concat(' OR (my_module_id IN(?))')
where_arguments << subjects_with_children[:my_module]
@ -71,8 +71,8 @@ class ActivitiesService
subjects_with_children = load_subjects_children('MyModule': [my_module.id])
query = Activity.where(project: my_module.experiment.project)
query.where(
subjects_with_children.map { '(subject_type = ? AND subject_id IN(?))' }.join(' OR '),
*subjects_with_children.flatten
subjects_with_children.to_h.map { '(subject_type = ? AND subject_id IN(?))' }.join(' OR '),
*subjects_with_children.to_h.flatten
)
end
end

View file

@ -9,17 +9,28 @@ module Experiments
def initialize(experiment:)
@exp = experiment
@graph = GraphViz.new(:G, type: :digraph, use: :neato)
@graph[:size] = '4,4'
@graph.node[color: Constants::COLOR_ALTO,
graph_params = {
type: :digraph,
use: :neato,
inputscale: 3,
size: '2,2',
pad: '0.4',
ratio: 'fill',
splines: true,
center: true,
pack: true,
bgcolor: Constants::COLOR_CONCRETE,
mode: 'ipsep'
}
@graph = GraphViz.new(:G, graph_params)
@graph.node[color: Constants::COLOR_VOLCANO,
style: :filled,
fontcolor: Constants::COLOR_VOLCANO,
shape: 'circle',
fontname: 'Arial',
fontsize: '16.0']
@graph.edge[color: Constants::COLOR_ALTO]
@graph.edge[color: Constants::COLOR_VOLCANO, penwidth: '3.0']
@errors = []
end
@ -36,20 +47,11 @@ module Experiments
private
def draw_diagram
# Draw orphan nodes
@exp.my_modules.without_group.each do |my_module|
@graph.subgraph(rank: 'same').add_nodes(
"Orphan-#{my_module.id}",
label: '',
pos: "#{my_module.x / 10},-#{my_module.y / 10}!"
)
end
# Draw grouped modules
subg = {}
@exp.my_module_groups.each_with_index do |group, gindex|
subgraph_id = "cluster-#{gindex}"
subg[subgraph_id] = @graph.subgraph(rank: 'same')
subg[subgraph_id] = @graph.subgraph
nodes = {}
group.my_modules.workflow_ordered.each_with_index do |my_module, index|
@ -71,6 +73,15 @@ module Experiments
end
end
end
# Draw orphan nodes
@exp.my_modules.without_group.each do |my_module|
@graph.subgraph.add_nodes(
"Orphan-#{my_module.id}",
label: '',
pos: "#{my_module.x / 10},-#{my_module.y / 10}!"
)
end
end
def save_file

View file

@ -20,15 +20,19 @@ module Experiments
ActiveRecord::Base.transaction do
@exp.project = @project
@exp.my_modules.each do |m|
new_tags = m.tags.map do |t|
t.clone_to_project_or_return_existing(@project)
@exp.my_modules.each do |my_module|
new_tags = []
my_module.tags.each do |tag|
new_tag = @project.tags.where.not(id: new_tags).find_by(name: tag.name, color: tag.color)
new_tag ||=
@project.tags.create!(name: tag.name, color: tag.color, created_by: @user, last_modified_by: @user)
new_tags << new_tag
end
m.my_module_tags.delete_all
m.tags = new_tags
my_module.tags.destroy_all
my_module.tags = new_tags
end
raise ActiveRecord::Rollback unless @exp.save
@exp.save!
end
@errors.merge!(@exp.errors.to_hash) unless @exp.valid?

View file

@ -0,0 +1,92 @@
# frozen_string_literal: true
class ExperimentsOverviewService
def initialize(project, user, params)
@project = project
@user = user
@params = params
@view_state = @project.current_view_state(@user)
@view_mode = if @project.archived?
'archived'
else
params[:view_mode] || 'active'
end
# Update sort if changed
@sort = @view_state.state.dig('experiments', @view_mode, 'sort') || 'atoz'
if @params[:sort] && @sort != @params[:sort] &&
%w(new old atoz ztoa archived_old archived_new).include?(@params[:sort])
@view_state.state['experiments'].merge!(Hash[@view_mode, { 'sort': @params[:sort] }.stringify_keys])
@view_state.save!
@sort = @view_state.state.dig('experiments', @view_mode, 'sort')
end
end
def experiments
sort_records(
filter_records(
fetch_records
)
)
end
private
def fetch_records
@project.experiments
.joins(:project)
.joins('LEFT OUTER JOIN my_modules AS active_tasks ON active_tasks.experiment_id = experiments.id ' \
'AND active_tasks.archived = FALSE')
.joins('LEFT OUTER JOIN my_modules AS active_completed_tasks ON active_completed_tasks.experiment_id '\
'= experiments.id AND active_completed_tasks.archived = FALSE AND active_completed_tasks.state = 1')
.select('experiments.*')
.select('COUNT(DISTINCT active_tasks.id) AS task_count')
.select('COUNT(DISTINCT active_completed_tasks.id) AS completed_task_count')
.group('experiments.id')
end
def filter_records(records)
records = records.archived if @view_mode == 'archived' && @project.active?
records = records.active if @view_mode == 'active'
if @params[:search].present?
records = records.where_attributes_like(%w(experiments.name experiments.description), @params[:search])
end
if @params[:created_on_from].present?
records = records.where('experiments.created_at > ?', @params[:created_on_from])
end
records = records.where('experiments.created_at < ?', @params[:created_on_to]) if @params[:created_on_to].present?
if @params[:updated_on_from].present?
records = records.where('experiments.updated_at > ?', @params[:updated_on_from])
end
records = records.where('experiments.updated_at < ?', @params[:updated_on_to]) if @params[:updated_on_to].present?
if @params[:archived_on_from].present?
records = records.where('COALESCE(experiments.archived_on, projects.archived_on) > ?', @params[:archived_on_from])
end
if @params[:archived_on_to].present?
records = records.where('COALESCE(experiments.archived_on, projects.archived_on) < ?', @params[:archived_on_to])
end
records
end
def sort_records(records)
case @sort
when 'new'
records.order(created_at: :desc)
when 'old'
records.order(:created_at)
when 'atoz'
records.order(:name)
when 'ztoa'
records.order(name: :desc)
when 'archived_old'
records.group('projects.archived_on')
.order(Arel.sql('COALESCE(experiments.archived_on, projects.archived_on) ASC'))
when 'archived_new'
records.group('projects.archived_on')
.order(Arel.sql('COALESCE(experiments.archived_on, projects.archived_on) DESC'))
else
records
end
end
end

View file

@ -110,8 +110,9 @@ class ProjectsOverviewService
records = records.archived if @view_mode == 'archived'
records = records.active if @view_mode == 'active'
records = records.where_attributes_like('projects.name', @params[:search]) if @params[:search].present?
records = records.where_attributes_like('projects.name', @params[:search]) if @params[:search].present?
records = records.joins(:user_projects).where('user_projects.user_id IN (?)', @params[:members]) if @params[:members]&.any?
if @params[:members].present?
records = records.joins(:user_projects).where('user_projects.user_id IN (?)', @params[:members])
end
records = records.where('projects.created_at > ?', @params[:created_on_from]) if @params[:created_on_from].present?
records = records.where('projects.created_at < ?', @params[:created_on_to]) if @params[:created_on_to].present?
records = records.where('projects.archived_on < ?', @params[:archived_on_to]) if @params[:archived_on_to].present?

View file

@ -11,7 +11,7 @@ module RepositoryActions
log_activity(:archive_inventory_item, row) if @log_activities
end
rescue ActiveRecord::RecordInvalid
@errors[:archiving_error] = I18n.t('repositories.archive_records.unsuccess_flash', @repository.name)
@errors[:archiving_error] = I18n.t('repositories.archive_records.unsuccess_flash', repository: @repository.name)
raise ActiveRecord::Rollback
end

View file

@ -11,7 +11,7 @@ module RepositoryActions
log_activity(:restore_inventory_item, row) if @log_activities
end
rescue ActiveRecord::RecordInvalid
@errors[:restoring_error] = I18n.t('repositories.restore_records.unsuccess_flash', @repository.name)
@errors[:restoring_error] = I18n.t('repositories.restore_records.unsuccess_flash', repository: @repository.name)
raise ActiveRecord::Rollback
end

View file

@ -10,7 +10,7 @@ class RepositoryDatatableService
@params = params
@sortable_columns = build_sortable_columns
create_columns_mappings
process_query
@repository_rows = process_query
end
private
@ -57,7 +57,7 @@ class RepositoryDatatableService
.select('COUNT(DISTINCT experiments.project_id) AS "assigned_projects_count"')
repository_rows = repository_rows.preload(Extends::REPOSITORY_ROWS_PRELOAD_RELATIONS)
@repository_rows = sort_rows(order_obj, repository_rows)
sort_rows(order_obj, repository_rows)
end
def fetch_rows(search_value)

View file

@ -57,7 +57,6 @@ class TeamImporter
team_last_modified_by = team.last_modified_by_id
team.last_modified_by_id = nil
team.without_templates = true
team.without_intro_demo = true
team.save!
create_users(team_json['users'], team)

View file

@ -1,60 +0,0 @@
# frozen_string_literal: true
module DelayedUploaderDemo
# Get asset from demo_files folder
def self.get_asset(user, team, file_name)
asset = Asset.create(
created_by: user,
team: team,
last_modified_by: user
)
asset.file.attach(io: File.open("#{Rails.root}/app/assets/demo_files/#{file_name}", 'r'), filename: file_name)
asset
end
# Generates results asset for given module, file_name assumes file is located
# in demo_files.
def self.generate_result_asset(
my_module:,
current_user:,
current_team:,
result_name:,
created_at: Time.now,
file_name:,
comment: nil
)
temp_asset = get_asset(current_user, current_team, file_name)
temp_result = Result.new(
created_at: created_at,
user: current_user,
my_module: my_module,
name: result_name,
asset: temp_asset
)
temp_result.save
# Generate comment if it exists
generate_result_comment(temp_result, current_user, comment) if comment
temp_asset.post_process_file(my_module.experiment.project.team)
end
# Adds asset to existing step
def self.add_step_asset(step:, current_user:, current_team:, file_name:)
temp_asset =
DelayedUploaderDemo.get_asset(current_user, current_team, file_name)
step.assets << temp_asset
temp_asset.post_process_file(step.my_module.experiment.project.team)
end
def self.generate_result_comment(result, user, message, created_at = nil)
created_at ||= result.created_at
ResultComment.create(
user: user,
message: message,
created_at: created_at,
result: result
)
end
end

File diff suppressed because it is too large Load diff

View file

@ -157,7 +157,7 @@ module ProtocolsImporter
# Decode the file bytes
file = StringIO.new(Base64.decode64(tiny_mce_img_json['bytes']))
to_blob = ActiveStorage::Blob.create_after_upload!(io: file,
to_blob = ActiveStorage::Blob.create_and_upload!(io: file,
filename: tiny_mce_img_json['fileName'],
content_type: tiny_mce_img_json['fileType'],
metadata: JSON.parse(tiny_mce_img_json['fileMetadata'] || '{}'))

View file

@ -30,12 +30,14 @@
</a>
</div>
<div class="task-selector-container pull-left" data-view-mode="archived" data-task-id="<%= my_module.id %>">
<div class="sci-checkbox-container">
<input value="1" type="checkbox" class="sci-checkbox task-selector">
<span class="sci-checkbox-label"></span>
<% unless my_module.experiment.archived_branch? %>
<div class="task-selector-container pull-left " data-view-mode="archived" data-task-id="<%= my_module.id %>">
<div class="sci-checkbox-container">
<input value="1" type="checkbox" class="sci-checkbox task-selector">
<span class="sci-checkbox-label"></span>
</div>
</div>
</div>
<% end %>
<div class="panel-heading">
<h3 class="panel-title">

View file

@ -51,7 +51,7 @@
data-disable-placeholder="<%= t("dashboard.current_tasks.filter.select_experiment") %>"
data-placeholder="<%= t("dashboard.current_tasks.filter.select_experiment") %>"></select>
</div>
<div class="footer">
<div class="footer center">
<div class="btn btn-primary apply-filters"><%= t("dashboard.current_tasks.filter.apply") %></div>
</div>
</div>

View file

@ -1,13 +1,7 @@
<!-- Clone experiment modal -->
<div class="modal"
id="clone-experiment-modal-<%= @experiment.id %>"
tabindex="-1"
role="dialog"
aria-labelledby="clone-experiment-modal-label">
<%= bootstrap_form_for @experiment, url: clone_experiment_path(),
method: :post do |f| %>
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal" id="clone-experiment-modal-<%= @experiment.id %>" tabindex="-1" role="dialog" aria-labelledby="clone-experiment-modal-label">
<div class="modal-dialog" role="document">
<div class="modal-content">
<%= bootstrap_form_for @experiment, url: clone_experiment_path(@experiment), method: :post do |f| %>
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="clone-experiment-modal-label"><%= t("experiments.clone.modal_title", experiment: @experiment.name ) %></h5>
@ -29,7 +23,7 @@
<button type="button" class="btn btn-secondary" data-dismiss="modal"><%=t "general.cancel" %></button>
<%= f.submit t("experiments.clone.modal_submit"), class: "btn btn-primary" %>
</div>
</div>
<% end %>
</div>
<% end %>
</div>
</div>

View file

@ -0,0 +1,20 @@
<div class="modal" id="experimentDetailsModal" tabindex="-1" role="dialog" aria-labelledby="experimentDetailsModalLabel">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="<%= t('general.close') %>"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="experimentDetailsModalLabel">
<%= t('experiments.index.details_modal.description', name: @experiment.name) %>
</h4>
</div>
<div class="modal-body">
<p>
<%= @experiment.description %>
</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal"><%= t('general.close') %></button>
</div>
</div>
</div>
</div>

View file

@ -29,6 +29,7 @@
<li><%= link_to t('experiments.archive.label_title'),
archive_experiment_url(experiment),
type: 'button',
method: :post,
data: { confirm: t('experiments.canvas.archive_confirm') } %></li>
<% end %>
<% if can_restore_experiment?(experiment) %>

View file

@ -1,24 +1,21 @@
<!-- Edit experiment modal -->
<div class="modal"
id="edit-experiment-modal-<%= @experiment.id %>"
tabindex="-1"
role="dialog"
aria-labelledby="edit-experiment-modal-label">
<%= bootstrap_form_for [@project, @experiment], remote: true do |f| %>
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal" id="edit-experiment-modal-<%= @experiment.id %>" tabindex="-1" role="dialog" aria-labelledby="edit-experiment-modal-label">
<div class="modal-dialog" role="document">
<div class="modal-content">
<%= bootstrap_form_for [@project, @experiment], remote: true, html: { class: 'experiment-action-form' } do |f| %>
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="edit-eperiment-modal-label"><%= t("experiments.edit.modal_title", experiment: @experiment.name ) %></h4>
<button type="button" class="close" data-dismiss="modal" aria-label="<%= t('general.close') %>"><span aria-hidden="true">&times;</span></button>
<h4 class="modal-title" id="edit-eperiment-modal-label">
<%= t('experiments.edit.modal_title', experiment: @experiment.name ) %>
</h4>
</div>
<div class="modal-body">
<%= render partial: "form.html.erb", locals: { form: f } %>
<%= render partial: 'form.html.erb', locals: { form: f } %>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal"><%=t "general.cancel" %></button>
<%= f.submit t("experiments.edit.modal_create"), class: "btn btn-primary" %>
<button type="button" class="btn btn-secondary" data-dismiss="modal"><%= t('general.cancel') %></button>
<%= f.submit t('experiments.edit.modal_create'), class: 'btn btn-primary' %>
</div>
</div>
<% end %>
</div>
<% end %>
</div>
</div>

View file

@ -2,10 +2,10 @@
<div class="col-xs-12 col-sm-12 col-md-12 col-lg-12">
<div class="form-group">
<%= form.text_field :name,
label: t("experiments.new.name"),
label: t('experiments.new.name'),
autofocus: true,
placeholder: t("experiments.new.name_placeholder"),
id: 'experiment-name'%>
placeholder: t('experiments.new.name_placeholder'),
id: 'experiment-name' %>
</div>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show more