mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-12-11 14:45:59 +08:00
Merge remote-tracking branch 'upstream/ux-release-1' into ml-sci-2118
This commit is contained in:
commit
884eef9d07
24 changed files with 946 additions and 781 deletions
|
|
@ -198,9 +198,6 @@ var HelperModule = (function(){
|
||||||
window.setTimeout(function () {
|
window.setTimeout(function () {
|
||||||
flash.fadeTo(500, 0).slideUp(500, function () {
|
flash.fadeTo(500, 0).slideUp(500, function () {
|
||||||
$(this).remove();
|
$(this).remove();
|
||||||
if($('.alert').length <= 0) {
|
|
||||||
$('#content-wrapper').removeClass('alert-shown');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
@ -234,7 +231,6 @@ var HelperModule = (function(){
|
||||||
'</div>' +
|
'</div>' +
|
||||||
'</div>';
|
'</div>';
|
||||||
$('#notifications').html(htmlSnippet);
|
$('#notifications').html(htmlSnippet);
|
||||||
$('#content-wrapper').addClass('alert-shown');
|
|
||||||
helpers.hideFlashMsg();
|
helpers.hideFlashMsg();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -94,20 +94,9 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function focusSearchInput() {
|
|
||||||
var searchIco = $('#search-ico');
|
|
||||||
searchIco
|
|
||||||
.on('shown.bs.dropdown', function() {
|
|
||||||
searchIco
|
|
||||||
.find('input.form-control')
|
|
||||||
.focus();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// init
|
// init
|
||||||
loadDropdownNotifications();
|
loadDropdownNotifications();
|
||||||
loadUnseenNotificationsNumber();
|
loadUnseenNotificationsNumber();
|
||||||
toggleNotificationBellPosition();
|
toggleNotificationBellPosition();
|
||||||
focusSearchInput();
|
|
||||||
initGlobalSwitchForm();
|
initGlobalSwitchForm();
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,6 @@ $(document).ready(function () {
|
||||||
$("#hide-alert").click(function(ev) {
|
$("#hide-alert").click(function(ev) {
|
||||||
$(this).closest("div.alert").addClass("alert-hidden");
|
$(this).closest("div.alert").addClass("alert-hidden");
|
||||||
$("#content-wrapper").addClass("alert-hidden");
|
$("#content-wrapper").addClass("alert-hidden");
|
||||||
$("#content-wrapper").removeClass("alert-shown");
|
|
||||||
|
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
|
|
|
||||||
|
|
@ -3,11 +3,13 @@
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
||||||
// Theme colors
|
// Theme colors
|
||||||
$color-theme-primary: #37a0d9;
|
$color-theme-primary: #41B0E0;
|
||||||
$color-theme-secondary: #8fd13f;
|
$color-theme-secondary: #8fd13f;
|
||||||
$color-theme-dark: #6d6e71;
|
$color-theme-dark: #6d6e71;
|
||||||
|
|
||||||
// Grayscale colors
|
// Grayscale colors
|
||||||
|
$color-border: #DDDDDD;
|
||||||
|
$color-list-separator: #BDC3C7;
|
||||||
$color-white: #fff;
|
$color-white: #fff;
|
||||||
$color-alabaster: #fcfcfc;
|
$color-alabaster: #fcfcfc;
|
||||||
$color-snow: #f9f9f9;
|
$color-snow: #f9f9f9;
|
||||||
|
|
@ -34,6 +36,7 @@ $color-mystic: #eaeff2;
|
||||||
$color-candlelight: #ffda23;
|
$color-candlelight: #ffda23;
|
||||||
$color-orange: #ff900b;
|
$color-orange: #ff900b;
|
||||||
$color-saturated-green: #008600;
|
$color-saturated-green: #008600;
|
||||||
|
$color-confirmation-green: #25AE88;
|
||||||
$color-blue-yadcf: #337ab7;
|
$color-blue-yadcf: #337ab7;
|
||||||
|
|
||||||
// Red colors
|
// Red colors
|
||||||
|
|
|
||||||
2
app/assets/stylesheets/extend/bootstrap.scss
vendored
2
app/assets/stylesheets/extend/bootstrap.scss
vendored
|
|
@ -58,7 +58,7 @@
|
||||||
float: none;
|
float: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 1000px) {
|
@media (max-width: 1188px) {
|
||||||
.navbar-header {
|
.navbar-header {
|
||||||
float: none;
|
float: none;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
114
app/assets/stylesheets/themes/buttons.scss
Normal file
114
app/assets/stylesheets/themes/buttons.scss
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
@import 'constants';
|
||||||
|
@import "mixins";
|
||||||
|
|
||||||
|
|
||||||
|
#reset-tutorial-btn {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: $color-theme-secondary;
|
||||||
|
border-color: darken($color-theme-secondary, 5%);
|
||||||
|
|
||||||
|
&.active,
|
||||||
|
&.focus,
|
||||||
|
&.active.focus {
|
||||||
|
background-color: darken($color-theme-secondary, 20%);
|
||||||
|
border-color: darken($color-theme-secondary, 25%);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: darken($color-theme-secondary, 25%);
|
||||||
|
border-color: darken($color-theme-secondary, 30%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active,
|
||||||
|
&:focus,
|
||||||
|
&:active:focus,
|
||||||
|
&:active:hover,
|
||||||
|
&:focus:hover,
|
||||||
|
&:active:focus:hover {
|
||||||
|
background-color: darken($color-theme-secondary, 20%);
|
||||||
|
border-color: darken($color-theme-secondary, 25%);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: darken($color-theme-secondary, 5%);
|
||||||
|
border-color: darken($color-theme-secondary, 10%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.btn-link-alt {
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
margin-right: 5px;
|
||||||
|
padding: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-invis-file {
|
||||||
|
display: none;
|
||||||
|
opacity: 0;
|
||||||
|
position: absolute;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-open-file {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
& > input[type=file] {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
min-width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
font-size: 100px;
|
||||||
|
text-align: right;
|
||||||
|
filter: alpha(opacity=0);
|
||||||
|
opacity: 0;
|
||||||
|
outline: none;
|
||||||
|
background: white;
|
||||||
|
cursor: inherit;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Add users modal */
|
||||||
|
.btn-group-existing-users {
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
label.btn {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
&.btn-title {
|
||||||
|
color: $color-white;
|
||||||
|
cursor: inherit;
|
||||||
|
background-color: $color-theme-primary;
|
||||||
|
|
||||||
|
&:focus, &:active, &:hover {
|
||||||
|
box-shadow: none;
|
||||||
|
background-color: $color-theme-primary;
|
||||||
|
border-color: $color-dark-gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-greyed {
|
||||||
|
background-color: $color-silver-chalice;
|
||||||
|
border-color: $color-silver-chalice;
|
||||||
|
color: $color-white;
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&:focus {
|
||||||
|
background-color: darken($color-silver-chalice, 15%);
|
||||||
|
border-color: darken($color-silver-chalice, 15%);
|
||||||
|
color: $color-white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sample group color picker */
|
||||||
|
.btn-group-sample-group-color {
|
||||||
|
.btn-group > .btn {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
380
app/assets/stylesheets/themes/main_navigation.scss
Normal file
380
app/assets/stylesheets/themes/main_navigation.scss
Normal file
|
|
@ -0,0 +1,380 @@
|
||||||
|
@import 'constants';
|
||||||
|
@import "mixins";
|
||||||
|
|
||||||
|
#main-nav {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notifications-dropdown {
|
||||||
|
.fa-bell {
|
||||||
|
font-size: 15px;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
#count-notifications {
|
||||||
|
background-color: $color-theme-primary;
|
||||||
|
border-radius: 5px;
|
||||||
|
color: $color-wild-sand;
|
||||||
|
display: none;
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 12px;
|
||||||
|
padding: 1px 6px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar {
|
||||||
|
border-radius: 0;
|
||||||
|
padding-right: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-default {
|
||||||
|
background-color: $color-white;
|
||||||
|
border-color: $color-alto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.navbar-default .navbar-brand {
|
||||||
|
& > img {
|
||||||
|
margin-top: -7px;
|
||||||
|
max-width: 55px;
|
||||||
|
max-height: 38px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-notifications {
|
||||||
|
max-height: 500px;
|
||||||
|
overflow-x: hidden;
|
||||||
|
overflow-y: scroll;
|
||||||
|
padding-bottom: 0;
|
||||||
|
padding-top: 0;
|
||||||
|
width: 450px;
|
||||||
|
word-wrap: break-word;
|
||||||
|
|
||||||
|
.notifications-no-recent {
|
||||||
|
padding-bottom: 10px;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
border-bottom: 1px solid $color-alto;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
padding-top: 10px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $color-concrete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.unseen {
|
||||||
|
border-left: 4px solid $color-theme-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-center {
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar {
|
||||||
|
top: 0px;
|
||||||
|
margin-top: 5px;
|
||||||
|
height: 45px;
|
||||||
|
width: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assignment {
|
||||||
|
background-color: $color-theme-primary;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: $color-wild-sand;
|
||||||
|
display: block;
|
||||||
|
font-size: 23px;
|
||||||
|
height: 45px;
|
||||||
|
padding-top: 5px;
|
||||||
|
width: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deliver {
|
||||||
|
background-color: $color-orange;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: $color-wild-sand;
|
||||||
|
display: block;
|
||||||
|
font-size: 23px;
|
||||||
|
height: 45px;
|
||||||
|
padding-top: 5px;
|
||||||
|
width: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.system-message {
|
||||||
|
background-color: $color-theme-secondary;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: $color-wild-sand;
|
||||||
|
display: block;
|
||||||
|
font-size: 23px;
|
||||||
|
height: 45px;
|
||||||
|
padding-top: 8px;
|
||||||
|
width: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.notifications-dropdown-header {
|
||||||
|
background-color: $color-theme-primary;
|
||||||
|
color: $color-wild-sand;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 8px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $color-white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notifications-dropdown-footer {
|
||||||
|
background-color: $color-mystic;
|
||||||
|
padding: 8px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification {
|
||||||
|
padding-right: 8px;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-menu {
|
||||||
|
padding-right: 0;
|
||||||
|
|
||||||
|
.nav {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-content {
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-container {
|
||||||
|
padding-left: 45px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#search-bar {
|
||||||
|
border-color: $color-border;
|
||||||
|
.btn-default {
|
||||||
|
background-color: $color-theme-primary;
|
||||||
|
border-color: $color-theme-primary;
|
||||||
|
color: $color-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Search */
|
||||||
|
.nav-search {
|
||||||
|
li.disabled {
|
||||||
|
opacity: .8;
|
||||||
|
|
||||||
|
.badge {
|
||||||
|
background-color: $color-emperor;
|
||||||
|
opacity: .8;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.repositories-team {
|
||||||
|
padding: 10px 15px;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: $color-theme-primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.repository-search {
|
||||||
|
padding-left: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.notification-settings-container {
|
||||||
|
margin-bottom: 50px;
|
||||||
|
margin-top: 50px;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-sm-4 {
|
||||||
|
padding-left: 5rem;
|
||||||
|
padding-top: .5rem;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-sm-2 {
|
||||||
|
padding-left: 3rem;
|
||||||
|
padding-top: .7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.col-sm-4 {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
padding-left: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-sm-2 {
|
||||||
|
padding-left: 1.8rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-group {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.assignment {
|
||||||
|
background-color: $color-theme-primary;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: $color-wild-sand;
|
||||||
|
display: block !important;
|
||||||
|
font-size: 15px;
|
||||||
|
height: 30px;
|
||||||
|
margin-right: 15px;
|
||||||
|
padding: 7px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
padding-top: 5px;
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.system-message {
|
||||||
|
background-color: $color-theme-secondary;
|
||||||
|
border-radius: 50%;
|
||||||
|
color: $color-wild-sand;
|
||||||
|
display: block !important;
|
||||||
|
font-size: 15px;
|
||||||
|
height: 30px;
|
||||||
|
margin-right: 15px;
|
||||||
|
padding: 8px;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
padding-top: 5px;
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.img-circle {
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global team switch
|
||||||
|
#team-switch {
|
||||||
|
word-wrap: break-word;
|
||||||
|
|
||||||
|
.team-name {
|
||||||
|
margin-left: 17px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.glyphicon-ok-sign {
|
||||||
|
color: $color-confirmation-green;
|
||||||
|
margin-left: -10px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.team-name-item {
|
||||||
|
border-bottom: 1px solid $color-list-separator;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
padding-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-default {
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
text-align: right;
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-group {
|
||||||
|
border-color: $color-border;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: $color-theme-primary;
|
||||||
|
border-color: $color-theme-primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.caret {
|
||||||
|
color: $color-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: block;
|
||||||
|
text-align: left;
|
||||||
|
word-wrap: break-word;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: $color-concrete;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $color-emperor;
|
||||||
|
display: block;
|
||||||
|
line-height: 1.6em;
|
||||||
|
padding: 3px 20px;
|
||||||
|
text-align: left;
|
||||||
|
text-decoration: none;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#nav-team-switch {
|
||||||
|
margin-left: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-nav-dropdown {
|
||||||
|
border: 1px solid $color-border;
|
||||||
|
padding: 10px 0 10px 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Alert
|
||||||
|
.alert {
|
||||||
|
border-radius: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
opacity: .86;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&.alert-hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a#hide-alert {
|
||||||
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.alert-floating {
|
||||||
|
position: fixed;
|
||||||
|
top: 50px;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#content-wrapper {
|
||||||
|
margin-top: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// reset margins on small screens
|
||||||
|
@media (max-width: 1188px) {
|
||||||
|
|
||||||
|
#nav-team-switch {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
@import 'constants';
|
@import 'constants';
|
||||||
@import "mixins";
|
@import "mixins";
|
||||||
|
@import "main_navigation";
|
||||||
|
@import "buttons";
|
||||||
|
|
||||||
/** Layout **/
|
/** Layout **/
|
||||||
|
|
||||||
|
|
@ -27,10 +29,6 @@ table {
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#main-nav {
|
|
||||||
margin-bottom: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#project-archive-btn {
|
#project-archive-btn {
|
||||||
margin-left: 15px;
|
margin-left: 15px;
|
||||||
}
|
}
|
||||||
|
|
@ -60,159 +58,16 @@ table {
|
||||||
margin-right: 0.5em;
|
margin-right: 0.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#content-wrapper {
|
|
||||||
margin-top: 50px;
|
|
||||||
margin-left: 83px;
|
|
||||||
|
|
||||||
&.alert-shown {
|
|
||||||
margin-top: 102px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.center-block-narrow {
|
.center-block-narrow {
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#notifications-dropdown {
|
|
||||||
.fa-bell {
|
|
||||||
font-size: 15px;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
#count-notifications {
|
|
||||||
background-color: $color-theme-primary;
|
|
||||||
border-radius: 5px;
|
|
||||||
color: $color-wild-sand;
|
|
||||||
display: none;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin-left: 12px;
|
|
||||||
padding: 1px 6px;
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media(max-width:450px) {
|
@media(max-width:450px) {
|
||||||
.dropdown-notifications {
|
.dropdown-notifications {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dropdown-notifications {
|
|
||||||
max-height: 500px;
|
|
||||||
overflow-x: hidden;
|
|
||||||
overflow-y: scroll;
|
|
||||||
padding-bottom: 0;
|
|
||||||
padding-top: 0;
|
|
||||||
width: 450px;
|
|
||||||
word-wrap: break-word;
|
|
||||||
|
|
||||||
.notifications-no-recent {
|
|
||||||
padding-bottom: 10px;
|
|
||||||
padding-left: 10px;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification {
|
|
||||||
border-bottom: 1px solid $color-alto;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
padding-top: 10px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: $color-concrete;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.unseen {
|
|
||||||
border-left: 4px solid $color-theme-primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-center {
|
|
||||||
margin-left: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.avatar {
|
|
||||||
top: 0px;
|
|
||||||
margin-top: 5px;
|
|
||||||
height: 45px;
|
|
||||||
width: 45px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.assignment {
|
|
||||||
background-color: $color-theme-primary;
|
|
||||||
border-radius: 50%;
|
|
||||||
color: $color-wild-sand;
|
|
||||||
display: block;
|
|
||||||
font-size: 23px;
|
|
||||||
height: 45px;
|
|
||||||
padding-top: 5px;
|
|
||||||
width: 45px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.deliver {
|
|
||||||
background-color: $color-orange;
|
|
||||||
border-radius: 50%;
|
|
||||||
color: $color-wild-sand;
|
|
||||||
display: block;
|
|
||||||
font-size: 23px;
|
|
||||||
height: 45px;
|
|
||||||
padding-top: 5px;
|
|
||||||
width: 45px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.system-message {
|
|
||||||
background-color: $color-theme-secondary;
|
|
||||||
border-radius: 50%;
|
|
||||||
color: $color-wild-sand;
|
|
||||||
display: block;
|
|
||||||
font-size: 23px;
|
|
||||||
height: 45px;
|
|
||||||
padding-top: 8px;
|
|
||||||
width: 45px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.notifications-dropdown-header {
|
|
||||||
background-color: $color-theme-primary;
|
|
||||||
color: $color-wild-sand;
|
|
||||||
font-weight: bold;
|
|
||||||
padding: 8px;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: $color-white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.notifications-dropdown-footer {
|
|
||||||
background-color: $color-mystic;
|
|
||||||
padding: 8px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.notification {
|
|
||||||
padding-right: 8px;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
#search-menu {
|
|
||||||
padding-right: 0;
|
|
||||||
|
|
||||||
.nav {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#search-content {
|
|
||||||
padding-left: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#search-container {
|
|
||||||
padding-left: 45px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.well-search-checklist {
|
.well-search-checklist {
|
||||||
background-color: $color-concrete !important;
|
background-color: $color-concrete !important;
|
||||||
margin-bottom: 5px;
|
margin-bottom: 5px;
|
||||||
|
|
@ -253,27 +108,6 @@ a {
|
||||||
background-color: inherit;
|
background-color: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
.alert {
|
|
||||||
border-radius: 0;
|
|
||||||
margin-bottom: 0;
|
|
||||||
opacity: 1;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
&.alert-hidden {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a#hide-alert {
|
|
||||||
margin-left: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.alert-floating {
|
|
||||||
position: fixed;
|
|
||||||
top: 50px;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
background-color: $color-theme-primary;
|
background-color: $color-theme-primary;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
|
@ -302,76 +136,6 @@ a {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
|
||||||
border-radius: 1.5em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-primary {
|
|
||||||
background-color: $color-theme-secondary;
|
|
||||||
border-color: darken($color-theme-secondary, 5%);
|
|
||||||
|
|
||||||
&.active,
|
|
||||||
&.focus,
|
|
||||||
&.active.focus {
|
|
||||||
background-color: darken($color-theme-secondary, 20%);
|
|
||||||
border-color: darken($color-theme-secondary, 25%);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: darken($color-theme-secondary, 25%);
|
|
||||||
border-color: darken($color-theme-secondary, 30%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&:active,
|
|
||||||
&:focus,
|
|
||||||
&:active:focus,
|
|
||||||
&:active:hover,
|
|
||||||
&:focus:hover,
|
|
||||||
&:active:focus:hover {
|
|
||||||
background-color: darken($color-theme-secondary, 20%);
|
|
||||||
border-color: darken($color-theme-secondary, 25%);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: darken($color-theme-secondary, 5%);
|
|
||||||
border-color: darken($color-theme-secondary, 10%);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.btn-link-alt {
|
|
||||||
border-radius: 4px;
|
|
||||||
cursor: pointer;
|
|
||||||
margin-right: 5px;
|
|
||||||
padding: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-invis-file {
|
|
||||||
display: none;
|
|
||||||
opacity: 0;
|
|
||||||
position: absolute;
|
|
||||||
z-index: -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-open-file {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
& > input[type=file] {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
min-width: 100%;
|
|
||||||
min-height: 100%;
|
|
||||||
font-size: 100px;
|
|
||||||
text-align: right;
|
|
||||||
filter: alpha(opacity=0);
|
|
||||||
opacity: 0;
|
|
||||||
outline: none;
|
|
||||||
background: white;
|
|
||||||
cursor: inherit;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mark,.mark {
|
mark,.mark {
|
||||||
background-color: $color-candlelight;
|
background-color: $color-candlelight;
|
||||||
}
|
}
|
||||||
|
|
@ -394,43 +158,6 @@ mark,.mark {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar {
|
|
||||||
border-radius: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-default {
|
|
||||||
background-color: $color-white;
|
|
||||||
border-color: $color-alto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.navbar-default .navbar-brand {
|
|
||||||
background-color: $color-theme-primary;
|
|
||||||
font-size: 23px;
|
|
||||||
|
|
||||||
& > img {
|
|
||||||
margin-top: -4px;
|
|
||||||
max-width: 132px;
|
|
||||||
max-height: 26px;
|
|
||||||
|
|
||||||
&.with-version {
|
|
||||||
margin-top: -10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& > span.version {
|
|
||||||
font-size: 0.6em;
|
|
||||||
color: $color-white;
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus,
|
|
||||||
&:focus:active,
|
|
||||||
&:focus:visited {
|
|
||||||
background-color: $color-theme-primary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
a[data-toggle="tooltip"] {
|
a[data-toggle="tooltip"] {
|
||||||
color: inherit;
|
color: inherit;
|
||||||
border-bottom: 1px dashed $color-emperor;
|
border-bottom: 1px dashed $color-emperor;
|
||||||
|
|
@ -587,85 +314,6 @@ a[data-toggle="tooltip"] {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Search */
|
|
||||||
.nav-search {
|
|
||||||
li.disabled {
|
|
||||||
opacity: 0.8;
|
|
||||||
|
|
||||||
.badge {
|
|
||||||
background-color: $color-emperor;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.repositories-team {
|
|
||||||
padding: 10px 15px;
|
|
||||||
|
|
||||||
&.active {
|
|
||||||
color: $color-theme-primary;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.repository-search {
|
|
||||||
padding-left: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-dropdown {
|
|
||||||
padding-right: 25px;
|
|
||||||
width: 250px;
|
|
||||||
|
|
||||||
input {
|
|
||||||
width: 230px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media(max-width:1000px) {
|
|
||||||
.search-dropdown {
|
|
||||||
width: 270px;
|
|
||||||
|
|
||||||
.form-group {
|
|
||||||
padding-left: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Global team switch
|
|
||||||
#team-switch {
|
|
||||||
border-left: 1px solid $color-alto;
|
|
||||||
border-right: 1px solid $color-alto;
|
|
||||||
word-wrap: break-word;
|
|
||||||
|
|
||||||
.dropdown-menu {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
display: block;
|
|
||||||
text-align: left;
|
|
||||||
word-wrap: break-word;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: $color-concrete;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: $color-emperor;
|
|
||||||
display: block;
|
|
||||||
line-height: 1.6em;
|
|
||||||
padding: 3px 20px;
|
|
||||||
text-align: left;
|
|
||||||
text-decoration: none;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Settings */
|
/** Settings */
|
||||||
.nav-settings {
|
.nav-settings {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
|
|
@ -691,78 +339,6 @@ a[data-toggle="tooltip"] {
|
||||||
margin-top: 40px;
|
margin-top: 40px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.notification-settings-container {
|
|
||||||
margin-bottom: 50px;
|
|
||||||
margin-top: 50px;
|
|
||||||
|
|
||||||
h4 {
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 2rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-sm-4 {
|
|
||||||
padding-left: 5rem;
|
|
||||||
padding-top: .5rem;
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-sm-2 {
|
|
||||||
padding-left: 3rem;
|
|
||||||
padding-top: .7rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
|
||||||
.col-sm-4 {
|
|
||||||
margin-bottom: 1rem;
|
|
||||||
padding-left: 1.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.col-sm-2 {
|
|
||||||
padding-left: 1.8rem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.btn-group {
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.assignment {
|
|
||||||
background-color: $color-theme-primary;
|
|
||||||
border-radius: 50%;
|
|
||||||
color: $color-wild-sand;
|
|
||||||
display: block !important;
|
|
||||||
font-size: 15px;
|
|
||||||
height: 30px;
|
|
||||||
margin-right: 15px;
|
|
||||||
padding: 7px;
|
|
||||||
padding-bottom: 5px;
|
|
||||||
padding-top: 5px;
|
|
||||||
width: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.system-message {
|
|
||||||
background-color: $color-theme-secondary;
|
|
||||||
border-radius: 50%;
|
|
||||||
color: $color-wild-sand;
|
|
||||||
display: block !important;
|
|
||||||
font-size: 15px;
|
|
||||||
height: 30px;
|
|
||||||
margin-right: 15px;
|
|
||||||
padding: 8px;
|
|
||||||
padding-bottom: 5px;
|
|
||||||
padding-top: 5px;
|
|
||||||
width: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.img-circle {
|
|
||||||
margin-right: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#reset-tutorial-btn {
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Help link
|
// Help link
|
||||||
#help-link {
|
#help-link {
|
||||||
padding: 13px;
|
padding: 13px;
|
||||||
|
|
@ -780,26 +356,6 @@ a[data-toggle="tooltip"] {
|
||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Add users modal */
|
|
||||||
.btn-group-existing-users {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
label.btn {
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
&.btn-title {
|
|
||||||
color: $color-white;
|
|
||||||
cursor: inherit;
|
|
||||||
background-color: $color-theme-primary;
|
|
||||||
|
|
||||||
&:focus, &:active, &:hover {
|
|
||||||
box-shadow: none;
|
|
||||||
background-color: $color-theme-primary;
|
|
||||||
border-color: $color-dark-gray;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.existing-users-smalltext {
|
.existing-users-smalltext {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
@ -1429,18 +985,6 @@ ul.content-module-activities {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn-greyed {
|
|
||||||
background-color: $color-silver-chalice;
|
|
||||||
border-color: $color-silver-chalice;
|
|
||||||
color: $color-white;
|
|
||||||
|
|
||||||
&:hover,
|
|
||||||
&:focus {
|
|
||||||
background-color: darken($color-silver-chalice, 15%);
|
|
||||||
border-color: darken($color-silver-chalice, 15%);
|
|
||||||
color: $color-white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* Data table */
|
/* Data table */
|
||||||
|
|
||||||
table.dataTable {
|
table.dataTable {
|
||||||
|
|
@ -1500,13 +1044,6 @@ table.dataTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Sample group color picker */
|
|
||||||
.btn-group-sample-group-color {
|
|
||||||
.btn-group > .btn {
|
|
||||||
border-radius: 0 !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#samples_length {
|
#samples_length {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,10 @@ class MyModuleRepositoryRow < ApplicationRecord
|
||||||
foreign_key: 'assigned_by_id',
|
foreign_key: 'assigned_by_id',
|
||||||
class_name: 'User',
|
class_name: 'User',
|
||||||
optional: true
|
optional: true
|
||||||
belongs_to :repository_row, optional: true
|
belongs_to :repository_row,
|
||||||
belongs_to :my_module, optional: true
|
optional: true,
|
||||||
|
inverse_of: :my_module_repository_rows
|
||||||
|
belongs_to :my_module, optional: true, inverse_of: :my_module_repository_rows
|
||||||
|
|
||||||
validates :repository_row, :my_module, presence: true
|
validates :repository_row, :my_module, presence: true
|
||||||
validates :repository_row, uniqueness: { scope: :my_module }
|
validates :repository_row, uniqueness: { scope: :my_module }
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
class Repository < ApplicationRecord
|
class Repository < ApplicationRecord
|
||||||
include SearchableModel
|
include SearchableModel
|
||||||
|
include RepositoryImportParser
|
||||||
|
|
||||||
belongs_to :team, optional: true
|
belongs_to :team, optional: true
|
||||||
belongs_to :created_by,
|
belongs_to :created_by,
|
||||||
|
|
@ -95,87 +96,8 @@ class Repository < ApplicationRecord
|
||||||
new_repo
|
new_repo
|
||||||
end
|
end
|
||||||
|
|
||||||
# Imports records
|
|
||||||
def import_records(sheet, mappings, user)
|
def import_records(sheet, mappings, user)
|
||||||
errors = false
|
importer = RepositoryImportParser::Importer.new(sheet, mappings, user, self)
|
||||||
columns = []
|
importer.run
|
||||||
name_index = -1
|
|
||||||
total_nr = 0
|
|
||||||
nr_of_added = 0
|
|
||||||
header_skipped = false
|
|
||||||
|
|
||||||
mappings.each.with_index do |(_k, value), index|
|
|
||||||
if value == '-1'
|
|
||||||
# Fill blank space, so our indices stay the same
|
|
||||||
columns << nil
|
|
||||||
name_index = index
|
|
||||||
else
|
|
||||||
columns << repository_columns.find_by_id(value)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Check for duplicate columns
|
|
||||||
col_compact = columns.compact
|
|
||||||
unless col_compact.map(&:id).uniq.length == col_compact.length
|
|
||||||
return { status: :error, nr_of_added: nr_of_added, total_nr: total_nr }
|
|
||||||
end
|
|
||||||
rows = SpreadsheetParser.spreadsheet_enumerator(sheet)
|
|
||||||
|
|
||||||
# Now we can iterate through record data and save stuff into db
|
|
||||||
rows.each do |row|
|
|
||||||
# Skip empty rows
|
|
||||||
next if row.empty?
|
|
||||||
unless header_skipped
|
|
||||||
header_skipped = true
|
|
||||||
next
|
|
||||||
end
|
|
||||||
total_nr += 1
|
|
||||||
|
|
||||||
row = SpreadsheetParser.parse_row(row, sheet)
|
|
||||||
|
|
||||||
record_row = RepositoryRow.new(name: row[name_index],
|
|
||||||
repository: self,
|
|
||||||
created_by: user,
|
|
||||||
last_modified_by: user)
|
|
||||||
record_row.transaction do
|
|
||||||
unless record_row.save
|
|
||||||
errors = true
|
|
||||||
raise ActiveRecord::Rollback
|
|
||||||
end
|
|
||||||
|
|
||||||
row_cell_values = []
|
|
||||||
|
|
||||||
row.each.with_index do |value, index|
|
|
||||||
if columns[index] && value
|
|
||||||
cell_value = RepositoryTextValue.new(
|
|
||||||
data: value,
|
|
||||||
created_by: user,
|
|
||||||
last_modified_by: user,
|
|
||||||
repository_cell_attributes: {
|
|
||||||
repository_row: record_row,
|
|
||||||
repository_column: columns[index]
|
|
||||||
}
|
|
||||||
)
|
|
||||||
unless cell_value.valid?
|
|
||||||
errors = true
|
|
||||||
raise ActiveRecord::Rollback
|
|
||||||
end
|
|
||||||
row_cell_values << cell_value
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if RepositoryTextValue.import(row_cell_values,
|
|
||||||
recursive: true,
|
|
||||||
validate: false).failed_instances.any?
|
|
||||||
errors = true
|
|
||||||
raise ActiveRecord::Rollback
|
|
||||||
end
|
|
||||||
nr_of_added += 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if errors
|
|
||||||
return { status: :error, nr_of_added: nr_of_added, total_nr: total_nr }
|
|
||||||
end
|
|
||||||
{ status: :ok, nr_of_added: nr_of_added, total_nr: total_nr }
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
class RepositoryCell < ActiveRecord::Base
|
class RepositoryCell < ActiveRecord::Base
|
||||||
belongs_to :repository_row
|
belongs_to :repository_row
|
||||||
belongs_to :repository_column
|
belongs_to :repository_column
|
||||||
belongs_to :value, polymorphic: true, dependent: :destroy
|
belongs_to :value, polymorphic: true,
|
||||||
|
inverse_of: :repository_cell,
|
||||||
|
dependent: :destroy
|
||||||
belongs_to :repository_text_value,
|
belongs_to :repository_text_value,
|
||||||
(lambda do
|
(lambda do
|
||||||
where(repository_cells: { value_type: 'RepositoryTextValue' })
|
where(repository_cells: { value_type: 'RepositoryTextValue' })
|
||||||
|
|
|
||||||
129
app/utilities/repository_import_parser/importer.rb
Normal file
129
app/utilities/repository_import_parser/importer.rb
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# handles the import of repository records
|
||||||
|
# requires 4 parameters:
|
||||||
|
# @sheet: the csv file with imported rows
|
||||||
|
# @mappings: mappings for columns
|
||||||
|
# @user: current_user
|
||||||
|
# @repository: the repository in which we import the items
|
||||||
|
module RepositoryImportParser
|
||||||
|
class Importer
|
||||||
|
def initialize(sheet, mappings, user, repository)
|
||||||
|
@columns = []
|
||||||
|
@name_index = -1
|
||||||
|
@total_new_rows = 0
|
||||||
|
@new_rows_added = 0
|
||||||
|
@header_skipped = false
|
||||||
|
@repository = repository
|
||||||
|
@sheet = sheet
|
||||||
|
@rows = SpreadsheetParser.spreadsheet_enumerator(@sheet)
|
||||||
|
@mappings = mappings
|
||||||
|
@user = user
|
||||||
|
@repository_columns = @repository.repository_columns
|
||||||
|
end
|
||||||
|
|
||||||
|
def run
|
||||||
|
fetch_columns
|
||||||
|
return check_for_duplicate_columns if check_for_duplicate_columns
|
||||||
|
import_rows!
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def fetch_columns
|
||||||
|
@mappings.each.with_index do |(_, value), index|
|
||||||
|
if value == '-1'
|
||||||
|
# Fill blank space, so our indices stay the same
|
||||||
|
@columns << nil
|
||||||
|
@name_index = index
|
||||||
|
else
|
||||||
|
@columns << @repository_columns.find_by_id(value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_for_duplicate_columns
|
||||||
|
col_compact = @columns.compact
|
||||||
|
if col_compact.map(&:id).uniq.length != col_compact.length
|
||||||
|
return { status: :error,
|
||||||
|
nr_of_added: @new_rows_added,
|
||||||
|
total_nr: @total_new_rows }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def import_rows!
|
||||||
|
errors = false
|
||||||
|
@rows.each do |row|
|
||||||
|
# Skip empty rows
|
||||||
|
next if row.empty?
|
||||||
|
unless @header_skipped
|
||||||
|
@header_skipped = true
|
||||||
|
next
|
||||||
|
end
|
||||||
|
@total_new_rows += 1
|
||||||
|
|
||||||
|
row = SpreadsheetParser.parse_row(row, @sheet)
|
||||||
|
record_row = new_repository_row(row)
|
||||||
|
record_row.transaction do
|
||||||
|
unless record_row.save
|
||||||
|
errors = true
|
||||||
|
raise ActiveRecord::Rollback
|
||||||
|
end
|
||||||
|
|
||||||
|
row_cell_values = []
|
||||||
|
|
||||||
|
row.each.with_index do |value, index|
|
||||||
|
column = @columns[index]
|
||||||
|
if column && value.present?
|
||||||
|
# uses RepositoryCellValueResolver to retrieve the correct value
|
||||||
|
cell_value_resolver =
|
||||||
|
RepositoryImportParser::RepositoryCellValueResolver.new(
|
||||||
|
column, @user, @repository
|
||||||
|
)
|
||||||
|
cell_value = cell_value_resolver.get_value(value, record_row)
|
||||||
|
unless cell_value.valid?
|
||||||
|
errors = true
|
||||||
|
raise ActiveRecord::Rollback
|
||||||
|
end
|
||||||
|
row_cell_values << cell_value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
unless import_to_database(row_cell_values)
|
||||||
|
errors = true
|
||||||
|
raise ActiveRecord::Rollback
|
||||||
|
end
|
||||||
|
@new_rows_added += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if errors
|
||||||
|
return { status: :error,
|
||||||
|
nr_of_added: @new_rows_added,
|
||||||
|
total_nr: @total_new_rows }
|
||||||
|
end
|
||||||
|
{ status: :ok, nr_of_added: @new_rows_added, total_nr: @total_new_rows }
|
||||||
|
end
|
||||||
|
|
||||||
|
def new_repository_row(row)
|
||||||
|
RepositoryRow.new(name: row[@name_index],
|
||||||
|
repository: @repository,
|
||||||
|
created_by: @user,
|
||||||
|
last_modified_by: @user)
|
||||||
|
end
|
||||||
|
|
||||||
|
def import_to_database(row_cell_values)
|
||||||
|
return false if RepositoryTextValue.import(
|
||||||
|
row_cell_values.select { |element| element.is_a? RepositoryTextValue },
|
||||||
|
recursive: true,
|
||||||
|
validate: false
|
||||||
|
).failed_instances.any?
|
||||||
|
return false if RepositoryListValue.import(
|
||||||
|
row_cell_values.select { |element| element.is_a? RepositoryListValue },
|
||||||
|
recursive: true,
|
||||||
|
validate: false
|
||||||
|
).failed_instances.any?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# this class is used to resolve the column data_type and assign
|
||||||
|
# it to the repository_row
|
||||||
|
module RepositoryImportParser
|
||||||
|
class RepositoryCellValueResolver
|
||||||
|
def initialize(column, user, repository)
|
||||||
|
@column = column
|
||||||
|
@user = user
|
||||||
|
@repository = repository
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_value(value, record_row)
|
||||||
|
return unless @column
|
||||||
|
send("new_#{@column.data_type.underscore}", value, record_row)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def new_repository_text_value(value, record_row)
|
||||||
|
RepositoryTextValue.new(data: value,
|
||||||
|
created_by: @user,
|
||||||
|
last_modified_by: @user,
|
||||||
|
repository_cell_attributes: {
|
||||||
|
repository_row: record_row,
|
||||||
|
repository_column: @column
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
def new_repository_list_value(value, record_row)
|
||||||
|
list_item = @column.repository_list_items.find_by_data(value)
|
||||||
|
list_item ||= create_repository_list_item(value)
|
||||||
|
RepositoryListValue.new(
|
||||||
|
created_by: @user,
|
||||||
|
last_modified_by: @user,
|
||||||
|
repository_list_item: list_item,
|
||||||
|
repository_cell_attributes: {
|
||||||
|
repository_row: record_row,
|
||||||
|
repository_column: @column
|
||||||
|
}
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_repository_list_item(value)
|
||||||
|
RepositoryListItem.create(
|
||||||
|
data: value,
|
||||||
|
created_by: @user,
|
||||||
|
last_modified_by: @user,
|
||||||
|
repository_column: @column,
|
||||||
|
repository: @repository
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -65,7 +65,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<%= render "shared/left_menu_bar" if user_signed_in? %>
|
<%= render "shared/left_menu_bar" if user_signed_in? %>
|
||||||
<div id="content-wrapper" class="<%= "alert-shown" if flash[:success] || flash[:error] || notice || alert %>">
|
<div id="content-wrapper">
|
||||||
<%= yield :content %>
|
<%= yield :content %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
<nav class="navbar navbar-default navbar-fixed-top" id="main-nav">
|
<nav class="navbar navbar-default navbar-fixed-top" id="main-nav">
|
||||||
<div class="container">
|
<div class="container-fluid">
|
||||||
|
|
||||||
<!-- header -->
|
<!-- header -->
|
||||||
<div class="navbar-header">
|
<div class="navbar-header">
|
||||||
|
|
@ -10,166 +10,105 @@
|
||||||
<span class="icon-bar"></span>
|
<span class="icon-bar"></span>
|
||||||
</button>
|
</button>
|
||||||
<%= link_to(root_path, class: 'navbar-brand', title: t('nav.label.scinote')) do %>
|
<%= link_to(root_path, class: 'navbar-brand', title: t('nav.label.scinote')) do %>
|
||||||
<%
|
<%= image_tag('/images/scinote_icon.jpg', id: 'logo') %>
|
||||||
show_version = !Rails.env.production?
|
|
||||||
if ENV['NAVBAR_SHOW_VERSION'].present?
|
|
||||||
show_version = YAML.load(ENV['NAVBAR_SHOW_VERSION'])
|
|
||||||
end
|
|
||||||
%>
|
|
||||||
<% if show_version %>
|
|
||||||
<%= image_tag('/images/logo.png', class: 'with-version', id: 'logo') %>
|
|
||||||
<span class="version">
|
|
||||||
<%= Scinote::Application::VERSION %>
|
|
||||||
</span>
|
|
||||||
<% else %>
|
|
||||||
<%= image_tag('/images/logo.png', id: 'logo') %>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<% if user_signed_in? %>
|
<% if user_signed_in? %>
|
||||||
<div class="collapse navbar-collapse" id="main-menu">
|
<div class="collapse navbar-collapse" id="main-menu">
|
||||||
<!-- links -->
|
<ul class="nav navbar-nav navbar-left" id="nav-team-switch">
|
||||||
<ul class="nav navbar-nav">
|
<!-- Global team switch -->
|
||||||
<li>
|
<% if current_user.teams.length > 0 %>
|
||||||
<a title="<%= t('nav.label.projects') %>" href="<%= projects_path %>">
|
<li id="team-switch">
|
||||||
<span class="glyphicon glyphicon-home"></span>
|
<div class="btn-group">
|
||||||
<span class="visible-xs-inline visible-sm-inline"><%= t('nav.label.projects') %></span>
|
<button type="button" class="btn btn-default"><%= truncate_team_name(current_team.name) %></button>
|
||||||
</a>
|
<button type="button"
|
||||||
</li>
|
class="btn btn-primary dropdown-toggle"
|
||||||
<% if current_team %>
|
title="<%= t('nav.label.teams') %>"
|
||||||
<li>
|
data-toggle="dropdown"
|
||||||
<a id="protocol-link" title="<%= t('nav.label.protocols') %>" href="<%= protocols_path %>">
|
aria-haspopup="true"
|
||||||
<span class="glyphicon glyphicon-list-alt"></span>
|
aria-expanded="false">
|
||||||
<span class="visible-xs-inline visible-sm-inline"><%= t('nav.label.protocols') %></span>
|
<span class="caret"></span>
|
||||||
</a>
|
<span class="sr-only">Toggle Dropdown</span>
|
||||||
</li>
|
</button>
|
||||||
<li>
|
<% if current_user.teams.length > 1 || can_create_teams? %>
|
||||||
<a id="repositories-link" title="<%= t('nav.label.repositories') %>" href="<%= team_repositories_path(current_team) %>">
|
<ul class="dropdown-menu">
|
||||||
<i class="fa fa-cubes" aria-hidden="true"></i>
|
<%= form_for(current_user,
|
||||||
<span class="visible-xs-inline visible-sm-inline"><%= t('nav.label.repositories') %></span>
|
url: user_current_team_path,
|
||||||
</a>
|
method: :post) do |f| %>
|
||||||
</li>
|
<%= hidden_field(:user, :current_team_id) %>
|
||||||
<% else %>
|
<% current_user.teams.each do |team| %>
|
||||||
<li class="disabled">
|
<li class="team-name-item">
|
||||||
<a id="protocol-link" title="<%= t('nav.label.protocols') %>" href="#">
|
<a href="#"
|
||||||
<span class="glyphicon glyphicon-list-alt"></span>
|
data-id="<%= team.id %>"
|
||||||
<span class="visible-xs-inline visible-sm-inline"><%= t('nav.label.protocols') %></span>
|
class="text-center change-team">
|
||||||
</a>
|
<% if current_team == team %>
|
||||||
</li>
|
<span class="glyphicon glyphicon-ok-sign"></span> <strong><%= team.name %></strong>
|
||||||
<li class="disabled">
|
<% else %>
|
||||||
<a id="repositories-link" title="<%= t('nav.label.repositories') %>" href="#">
|
<span class="team-name"><%= team.name %></span>
|
||||||
<span class="fa fa-cubes"></span>
|
<% end %>
|
||||||
<span class="visible-xs-inline visible-sm-inline"><%= t('nav.label.repositories') %></span>
|
</a>
|
||||||
</a>
|
</li>
|
||||||
</li>
|
<% end %>
|
||||||
|
<% end %>
|
||||||
|
<% if current_user.teams.length > 1 && can_create_teams? %>
|
||||||
|
<li>
|
||||||
|
<%= link_to new_team_path do %>
|
||||||
|
<span class="glyphicon glyphicon-plus"></span>
|
||||||
|
<%= t('users.settings.teams.index.new_team') %>
|
||||||
|
<% end %>
|
||||||
|
</li>
|
||||||
|
<% end %>
|
||||||
|
</ul>
|
||||||
|
<% end %>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
<% end %>
|
<% end %>
|
||||||
<% if false %>
|
|
||||||
<li>
|
|
||||||
<a href="#">
|
|
||||||
<span class="glyphicon glyphicon-calendar"></span>
|
|
||||||
<span class="visible-xs-inline visible-sm-inline"><%= t('nav.label.calendar') %></span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<% end %>
|
|
||||||
<li>
|
|
||||||
<a class="btn-activity" title="<%= t('nav.label.activities') %>" href="<%= activities_path %>" role="button">
|
|
||||||
<span class="glyphicon glyphicon-equalizer"></span>
|
|
||||||
<span class="visible-xs-inline visible-sm-inline"><%= t('nav.label.activities') %></span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<!-- profile info -->
|
<!-- profile info -->
|
||||||
<ul class="nav navbar-nav navbar-right">
|
<ul class="nav navbar-nav navbar-right">
|
||||||
|
<!-- search form -->
|
||||||
<!-- Global team switch -->
|
<li>
|
||||||
<% if current_user.teams.length > 0 %>
|
<%= form_tag search_path,
|
||||||
<li class="dropdown" id="team-switch">
|
method: :get,
|
||||||
<a href="#"
|
id: 'search-bar',
|
||||||
class="dropdown-toggle"
|
class: 'navbar-form',
|
||||||
title="<%= t('nav.label.teams') %>"
|
role: 'search' do %>
|
||||||
data-toggle="dropdown"
|
<div class="input-group">
|
||||||
role="button"
|
<input class="form-control"
|
||||||
aria-haspopup="true"
|
type="text"
|
||||||
aria-expanded="false">
|
name="q"
|
||||||
<%= fa_icon 'users' %>
|
placeholder="<%= t('nav.search') %>" />
|
||||||
<span>
|
<span class="input-group-btn">
|
||||||
<%= truncate_team_name(current_team.name) %>
|
<button class="btn btn-default" type="submit"><%=t 'nav.search_button' %></button>
|
||||||
</span>
|
</span>
|
||||||
</a>
|
</div>
|
||||||
|
|
||||||
<% if current_user.teams.length > 1 || can_create_teams? %>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
<%= form_for(current_user,
|
|
||||||
url: user_current_team_path,
|
|
||||||
method: :post) do |f| %>
|
|
||||||
<%= hidden_field(:user, :current_team_id) %>
|
|
||||||
<% current_user.teams.each do |team| %>
|
|
||||||
<% next unless team != current_team %>
|
|
||||||
<li>
|
|
||||||
<a href="#"
|
|
||||||
data-id="<%= team.id %>"
|
|
||||||
class="text-center change-team">
|
|
||||||
<%= team.name %>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<% end %>
|
|
||||||
<% end %>
|
|
||||||
<% if current_user.teams.length > 1 && can_create_teams? %>
|
|
||||||
<li role="separator"
|
|
||||||
class="divider"></li>
|
|
||||||
<li>
|
|
||||||
<%= link_to new_team_path do %>
|
|
||||||
<span class="glyphicon glyphicon-plus"></span>
|
|
||||||
<%= t('users.settings.teams.index.new_team') %>
|
|
||||||
<% end %>
|
|
||||||
</li>
|
|
||||||
<% end %>
|
|
||||||
</ul>
|
|
||||||
<% end %>
|
<% end %>
|
||||||
</li>
|
</li>
|
||||||
<% end %>
|
|
||||||
|
|
||||||
<!-- search -->
|
<!-- greetings -->
|
||||||
<li class="dropdown"
|
<li id="user-account-dropdown" class="dropdown">
|
||||||
id="search-ico">
|
|
||||||
<a href="#"
|
<a href="#"
|
||||||
class="dropdown-toggle"
|
class="dropdown-toggle"
|
||||||
title="<%= t('nav.label.search') %>"
|
title="<%= t('nav.label.account') %>"
|
||||||
data-toggle="dropdown"
|
data-toggle="dropdown"
|
||||||
role="button"
|
role="button"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
aria-expanded="false">
|
aria-expanded="false">
|
||||||
<span class="glyphicon glyphicon-search"></span>
|
<span>
|
||||||
<span class="visible-xs-inline visible-sm-inline"><%= t('nav.label.search') %></span>
|
<%= t('nav.user_greeting', full_name: current_user.full_name) %>
|
||||||
|
</span>
|
||||||
|
<%= image_tag avatar_path(current_user, :icon_small),
|
||||||
|
class: "avatar" %>
|
||||||
</a>
|
</a>
|
||||||
<ul class="dropdown-menu search-dropdown">
|
<ul class="dropdown-menu" data-hook="navigation-user-menu">
|
||||||
<li>
|
<li>
|
||||||
<!-- search form -->
|
<%= link_to t('nav.user.logout'),
|
||||||
<%= form_tag search_path,
|
destroy_user_session_path,
|
||||||
method: :get,
|
method: :delete %>
|
||||||
id: 'search-bar',
|
</li>
|
||||||
class: 'navbar-form navbar-right',
|
</ul>
|
||||||
role: 'search' do %>
|
|
||||||
<div class="form-group">
|
|
||||||
<div class="input-group">
|
|
||||||
<input class="form-control"
|
|
||||||
type="text"
|
|
||||||
name="q"
|
|
||||||
placeholder="<%= t('nav.search') %>">
|
|
||||||
<span class="input-group-btn visible-xs visible-sm">
|
|
||||||
<button id="search-button"
|
|
||||||
class="btn btn-default"
|
|
||||||
type="submit">
|
|
||||||
<span class="glyphicon glyphicon-menu-right"></span>
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<% end %>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<!-- notifications -->
|
<!-- notifications -->
|
||||||
|
|
@ -205,71 +144,6 @@
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<!-- help -->
|
|
||||||
<li class="dropdown">
|
|
||||||
<a href="#"
|
|
||||||
id="help-link"
|
|
||||||
class="dropdown-toggle"
|
|
||||||
title="<%= t('nav.label.info') %>"
|
|
||||||
data-toggle="dropdown"
|
|
||||||
role="button"
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-expanded="false">
|
|
||||||
<span class="glyphicon glyphicon-info-sign"></span>
|
|
||||||
<span class="visible-xs-inline visible-sm-inline"><%= t('nav.label.info') %></span>
|
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu" data-hook="navigation-help-menu">
|
|
||||||
<li><%= link_to t('nav.help.support'),
|
|
||||||
Constants::SUPPORT_URL,
|
|
||||||
target: "_blank" %></li>
|
|
||||||
<li><%= link_to t('nav.help.tutorials'),
|
|
||||||
Constants::TUTORIALS_URL,
|
|
||||||
target: "_blank" %></li>
|
|
||||||
<li><%= link_to t('nav.help.release_notes'),
|
|
||||||
Constants::RELEASE_NOTES_URL,
|
|
||||||
target: "_blank" %></li>
|
|
||||||
<li><%= link_to t('nav.help.premium'),
|
|
||||||
Constants::PREMIUM_URL,
|
|
||||||
target: "_blank" %></li>
|
|
||||||
<li><%= link_to t('nav.help.contact'),
|
|
||||||
Constants::CONTACT_URL,
|
|
||||||
target: "_blank" %></li>
|
|
||||||
<li role="separator" class="divider"></li>
|
|
||||||
<li>
|
|
||||||
<%= link_to '#', data: { trigger: 'about-modal' } do %>
|
|
||||||
<%= t('nav.help.about') %>
|
|
||||||
<% end %>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<!-- greetings -->
|
|
||||||
<li id="user-account-dropdown" class="dropdown">
|
|
||||||
<a href="#"
|
|
||||||
class="dropdown-toggle"
|
|
||||||
title="<%= t('nav.label.account') %>"
|
|
||||||
data-toggle="dropdown"
|
|
||||||
role="button"
|
|
||||||
aria-haspopup="true"
|
|
||||||
aria-expanded="false">
|
|
||||||
<span>
|
|
||||||
<%= t('nav.user_greeting', full_name: current_user.full_name) %>
|
|
||||||
</span>
|
|
||||||
<%= image_tag avatar_path(current_user, :icon_small),
|
|
||||||
class: "avatar" %>
|
|
||||||
</a>
|
|
||||||
<ul class="dropdown-menu" data-hook="navigation-user-menu">
|
|
||||||
<li>
|
|
||||||
<%= link_to t('nav.user.settings'), edit_user_registration_path, data: { turbolinks: false } %>
|
|
||||||
</li>
|
|
||||||
<li role="separator" class="divider"></li>
|
|
||||||
<li>
|
|
||||||
<%= link_to t('nav.user.logout'),
|
|
||||||
destroy_user_session_path,
|
|
||||||
method: :delete %>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,8 @@ en:
|
||||||
body:
|
body:
|
||||||
notice: "You need to enable JavaScript to run this app."
|
notice: "You need to enable JavaScript to run this app."
|
||||||
nav:
|
nav:
|
||||||
search: "Search"
|
search: "Search for something..."
|
||||||
|
search_button: 'Go!'
|
||||||
user_greeting: "Hi, %{full_name}"
|
user_greeting: "Hi, %{full_name}"
|
||||||
advanced_search: "Advanced search"
|
advanced_search: "Advanced search"
|
||||||
title: "sciNote"
|
title: "sciNote"
|
||||||
|
|
@ -986,12 +987,12 @@ en:
|
||||||
|
|
||||||
repository_row:
|
repository_row:
|
||||||
modal_info:
|
modal_info:
|
||||||
head_title: "Information for record '%{repository_row}'"
|
head_title: "Information for item '%{repository_row}'"
|
||||||
added_on: "Added on"
|
added_on: "Added on"
|
||||||
added_by: "Added by"
|
added_by: "Added by"
|
||||||
custom_field: "%{cf}: "
|
custom_field: "%{cf}: "
|
||||||
title: "This record is assigned to %{nr} tasks."
|
title: "This item is assigned to %{nr} tasks."
|
||||||
no_tasks: "This record in not assigned to any task."
|
no_tasks: "This item in not assigned to any task."
|
||||||
samples:
|
samples:
|
||||||
columns: "Columns"
|
columns: "Columns"
|
||||||
columns_visibility: "Toggle visibility"
|
columns_visibility: "Toggle visibility"
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ Scenario: Unsuccessful Log in
|
||||||
And I click "Log in" button
|
And I click "Log in" button
|
||||||
Then I should see "Invalid Email or password." flash message
|
Then I should see "Invalid Email or password." flash message
|
||||||
|
|
||||||
@javascript
|
@javascript @wip
|
||||||
Scenario: Successful Log out
|
Scenario: Successful Log out
|
||||||
Given "night.slarker@gmail.com" is signed in with "mypassword1234"
|
Given "night.slarker@gmail.com" is signed in with "mypassword1234"
|
||||||
And I'm on the home page of "BioSistemika Process" team
|
And I'm on the home page of "BioSistemika Process" team
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ Background:
|
||||||
And "nonadmin@myorg.com" is in "BioSistemika Process" team as a "normal_user"
|
And "nonadmin@myorg.com" is in "BioSistemika Process" team as a "normal_user"
|
||||||
And "nonadmin@myorg.com" is signed in with "mypassword1234"
|
And "nonadmin@myorg.com" is signed in with "mypassword1234"
|
||||||
|
|
||||||
@javascript
|
@javascript @wip
|
||||||
Scenario: Successful navigate to profile page
|
Scenario: Successful navigate to profile page
|
||||||
Given I'm on the home page of "BioSistemika Process" team
|
Given I'm on the home page of "BioSistemika Process" team
|
||||||
And I click on Avatar
|
And I click on Avatar
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ require 'capybara/email'
|
||||||
Capybara.register_driver :poltergeist do |app|
|
Capybara.register_driver :poltergeist do |app|
|
||||||
options = {
|
options = {
|
||||||
# inspector: true,
|
# inspector: true,
|
||||||
screen_size: [1200, 900],
|
screen_size: [2560, 900],
|
||||||
js_errors: false,
|
js_errors: false,
|
||||||
timeout: 30,
|
timeout: 30,
|
||||||
phantomjs: Phantomjs.path,
|
phantomjs: Phantomjs.path,
|
||||||
|
|
|
||||||
BIN
public/images/scinote_icon.jpg
Normal file
BIN
public/images/scinote_icon.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6 KiB |
6
spec/fixtures/files/export.csv
vendored
Normal file
6
spec/fixtures/files/export.csv
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
Name,Added on,Added by,Sample group,Sample type,Custom items
|
||||||
|
Sample 5,02.03.2018 09:53,Admin,group 3,,test 3
|
||||||
|
Sample 4,02.03.2018 09:53,Admin,group 2,type 1,test 2
|
||||||
|
Sample 3,02.03.2018 09:53,Admin,,type 1,
|
||||||
|
Sample 2,02.03.2018 09:52,Admin,group 2,type 2,
|
||||||
|
Sample 1,02.03.2018 09:52,Admin,group 1,type 2,test test
|
||||||
|
91
spec/utilities/repository_import_parser/importer_spec.rb
Normal file
91
spec/utilities/repository_import_parser/importer_spec.rb
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe RepositoryImportParser::Importer do
|
||||||
|
let(:user) { create :user }
|
||||||
|
let(:team) { create :team, created_by: user }
|
||||||
|
let(:user_team) { create :user_team, user: user, team: team }
|
||||||
|
let(:repository) { create :repository, team: team, created_by: user }
|
||||||
|
let!(:sample_group_column) do
|
||||||
|
create :repository_column, repository: repository,
|
||||||
|
created_by: user,
|
||||||
|
name: 'Sample group',
|
||||||
|
data_type: 'RepositoryListValue'
|
||||||
|
end
|
||||||
|
let!(:sample_type_column) do
|
||||||
|
create :repository_column, repository: repository,
|
||||||
|
created_by: user,
|
||||||
|
name: 'Sample type',
|
||||||
|
data_type: 'RepositoryListValue'
|
||||||
|
end
|
||||||
|
let!(:custom_column) do
|
||||||
|
create :repository_column, repository: repository,
|
||||||
|
created_by: user,
|
||||||
|
name: 'Custom items',
|
||||||
|
data_type: 'RepositoryTextValue'
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:sheet) do
|
||||||
|
SpreadsheetParser.open_spreadsheet(fixture_file_upload('files/export.csv'))
|
||||||
|
end
|
||||||
|
let(:mappings) do
|
||||||
|
{ '0' => '-1',
|
||||||
|
'1' => '',
|
||||||
|
'2' => '',
|
||||||
|
'3' => sample_group_column.id.to_s,
|
||||||
|
'4' => sample_type_column.id.to_s,
|
||||||
|
'5' => custom_column.id.to_s }
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#run/0' do
|
||||||
|
let(:subject) do
|
||||||
|
RepositoryImportParser::Importer.new(sheet, mappings, user, repository)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'return a message of imported records' do
|
||||||
|
expect(subject.run).to eq({ status: :ok, nr_of_added: 5, total_nr: 5 })
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'generate 5 new repository rows' do
|
||||||
|
subject.run
|
||||||
|
expect(repository.repository_rows.count).to eq 5
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'generate 3 new repository list items on Sample group column' do
|
||||||
|
subject.run
|
||||||
|
column = repository.repository_columns.find_by_name('Sample group')
|
||||||
|
expect(column.repository_list_items.count).to eq 3
|
||||||
|
column.repository_list_items.each do |repository_item|
|
||||||
|
expect(['group 1', 'group 2', 'group 3']).to include(repository_item.data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'generate 2 new repository list items on Sample type column' do
|
||||||
|
subject.run
|
||||||
|
column = repository.repository_columns.find_by_name('Sample type')
|
||||||
|
expect(column.repository_list_items.count).to eq 2
|
||||||
|
column.repository_list_items.each do |repository_item|
|
||||||
|
expect(['type 1', 'type 2']).to include(repository_item.data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'assign custom columns to imported repository row' do
|
||||||
|
subject.run
|
||||||
|
row = repository.repository_rows.find_by_name('Sample 1')
|
||||||
|
sample_group_cell = row.repository_cells
|
||||||
|
.find_by_repository_column_id(
|
||||||
|
sample_group_column.id
|
||||||
|
)
|
||||||
|
sample_type_cell = row.repository_cells
|
||||||
|
.find_by_repository_column_id(
|
||||||
|
sample_type_column.id
|
||||||
|
)
|
||||||
|
custom_column_cell = row.repository_cells
|
||||||
|
.find_by_repository_column_id(
|
||||||
|
custom_column.id
|
||||||
|
)
|
||||||
|
expect(sample_group_cell.value.formatted).to eq 'group 1'
|
||||||
|
expect(sample_type_cell.value.formatted).to eq 'type 2'
|
||||||
|
expect(custom_column_cell.value.formatted).to eq 'test test'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe RepositoryImportParser::RepositoryCellValueResolver do
|
||||||
|
let(:user) { create :user }
|
||||||
|
let(:team) { create :team, created_by: user }
|
||||||
|
let(:user_team) { create :user_team, user: user, team: team }
|
||||||
|
let(:repository) { create :repository, team: team, created_by: user }
|
||||||
|
let!(:sample_group_column) do
|
||||||
|
create :repository_column, repository: repository,
|
||||||
|
created_by: user,
|
||||||
|
name: 'Sample group',
|
||||||
|
data_type: 'RepositoryListValue'
|
||||||
|
end
|
||||||
|
let!(:custom_column) do
|
||||||
|
create :repository_column, repository: repository,
|
||||||
|
created_by: user,
|
||||||
|
name: 'Custom items',
|
||||||
|
data_type: 'RepositoryTextValue'
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:repository_row) do
|
||||||
|
create :repository_row, repository: repository,
|
||||||
|
created_by: user,
|
||||||
|
name: 'Sample'
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#ruget_valuen/2' do
|
||||||
|
context 'RepositoryListValue' do
|
||||||
|
let(:subject) do
|
||||||
|
RepositoryImportParser::RepositoryCellValueResolver.new(
|
||||||
|
sample_group_column, user, repository
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a valid RepositoryListValue object' do
|
||||||
|
value = subject.get_value('leaf', repository_row)
|
||||||
|
expect(value).to be_valid
|
||||||
|
expect(value).to be_a RepositoryListValue
|
||||||
|
expect(value.formatted).to eq 'leaf'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates a new RepositoryListItem' do
|
||||||
|
value = subject.get_value('leaf', repository_row)
|
||||||
|
item = sample_group_column.repository_list_items.find_by_data('leaf')
|
||||||
|
expect(sample_group_column.repository_list_items.count).to eq 1
|
||||||
|
expect(item).to be_present
|
||||||
|
expect(item.data).to eq 'leaf'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'RepositoryTextValue' do
|
||||||
|
let(:subject) do
|
||||||
|
RepositoryImportParser::RepositoryCellValueResolver.new(
|
||||||
|
custom_column, user, repository
|
||||||
|
)
|
||||||
|
|
||||||
|
it 'returns a valid RepositoryTextValue object' do
|
||||||
|
value = subject.get_value('blood', repository_row)
|
||||||
|
expect(value).to be_valid
|
||||||
|
expect(value).to be_a RepositoryTextValue
|
||||||
|
expect(value.formatted).to eq 'blood'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
Add table
Reference in a new issue