+
Welcome to Topcoder Connect
We are temporarily down for maintenance. We will be back shortly, after Coder is done with all the amazing optimizations.
diff --git a/src/components/Maintenance/Maintenance.scss b/src/components/Maintenance/Maintenance.scss
index 6c3fcd6e1..7a8adf48d 100644
--- a/src/components/Maintenance/Maintenance.scss
+++ b/src/components/Maintenance/Maintenance.scss
@@ -8,15 +8,14 @@
}
}
- .page-error {
+ .page-error-maintenance {
border-radius:4px;
position: relative;
padding-top: 55px;
text-align: center;
min-height: 606px;
- min-width: 768px;
- width: 970px;
margin: 20px auto 0;
+ background: #ffffff;
/* background: $tc-white url('../../assets/icons/coder-broken.svg') no-repeat 18% 85%; */
background-size: 307px 300px;
a{
@@ -30,17 +29,25 @@
@include roboto-medium;
font-size: 48px;
letter-spacing: 0px;
- padding: 0 168px 25px 168px;
+ padding: 0 28px 25px 28px;
line-height: inherit;
+ @media screen and (max-width: $screen-sm - 1px) {
+ font-size: 30px;
+ }
}
p{
- text-align: left;
+ text-align: center;
padding: 0 168px;
@include roboto;
font-size: $tc-label-lg;
color: $tc-gray-70;
letter-spacing: 0px;
line-height: 23px;
+ padding: 0 28px 25px 28px;
+
+ @media screen and (max-width: $screen-md - 1px) {
+ text-align: justify;
+ }
}
span{
position: absolute;
diff --git a/src/components/StatusFilters/StatusFiltersMobile.jsx b/src/components/StatusFilters/StatusFiltersMobile.jsx
index 6163a2566..defacbe44 100644
--- a/src/components/StatusFilters/StatusFiltersMobile.jsx
+++ b/src/components/StatusFilters/StatusFiltersMobile.jsx
@@ -47,7 +47,7 @@ class StatusFiltersMobile extends React.Component {
return (
-
{currentSatusLabel}
+
{currentSatusLabel}
{isOpen &&
diff --git a/src/components/StatusFilters/StatusFiltersMobile.scss b/src/components/StatusFilters/StatusFiltersMobile.scss
index cb6bab593..9daf936a6 100644
--- a/src/components/StatusFilters/StatusFiltersMobile.scss
+++ b/src/components/StatusFilters/StatusFiltersMobile.scss
@@ -8,11 +8,11 @@
display: flex;
height: 10 * $base-unit;
font-size: 15px;
- justify-content: space-between;
padding: 0 4 * $base-unit;
> svg {
width: 12px;
+ margin-left: 10px;
}
&.is-open > svg {
diff --git a/src/config/constants.js b/src/config/constants.js
index c74c1de2a..85b5aa8b6 100644
--- a/src/config/constants.js
+++ b/src/config/constants.js
@@ -399,3 +399,11 @@ export const SCREEN_BREAKPOINT_RG = 992
export const SCREEN_BREAKPOINT_MD = 768
export const SCREEN_BREAKPOINT_SM = 640
export const SCREEN_BREAKPOINT_XS = 320
+
+export const NOTIFICATION_SETTINGS_PERIODS = [
+ { text: 'Send as they happen', value: 'immediately' },
+ { text: 'Every 10m.', value: 'every10minutes' },
+ { text: 'Hourly', value: 'hourly' },
+ { text: 'Daily', value: 'daily' },
+ { text: 'Weekly', value: 'weekly' },
+]
diff --git a/src/config/projectQuestions/enterprise_mobile.v1.0.js b/src/config/projectQuestions/enterprise_mobile.v1.0.js
index 3f33fe16a..9e945ad51 100644
--- a/src/config/projectQuestions/enterprise_mobile.v1.0.js
+++ b/src/config/projectQuestions/enterprise_mobile.v1.0.js
@@ -53,7 +53,7 @@ const sections = [
{ value: 'ios', label: 'iOS App - An app built for iPhone or iPads' },
{ value: 'android', label: 'Android App - An app built for mobile phones or tablets running Android.' },
{ value: 'hybrid', label: 'Hybrid App - An app built using a hybrid framework (ex. Ionic/Cordova/Xamarin) and exported to one or more operating systems (iOS, Android or both).' },
- { value: 'web', label: 'Mobile Web App - An app that is accessed by using a mobile web browser like Safari or Chrome.' }
+ { value: 'web', label: 'Progressive Mobile Web App - An app that is accessed by using a mobile web browser like Safari or Chrome, but that provides a native mobile app experience (rich features, i.e access device features such as GPS)' }
]
},
{
@@ -75,7 +75,7 @@ const sections = [
{
icon: 'question',
title: 'Form Factor/Orientation',
- description: 'Please place an X in the Required column for each \
+ description: 'Please select each for each \
form factor/orientation that must be supported.',
fieldName: 'details.appDefinition.formFactor',
type: 'checkbox-group',
@@ -92,7 +92,7 @@ const sections = [
{
required: false,
hideTitle: false,
- title: 'Style Guide & Brand Guidelines',
+ title: 'Style Guide & Brand Guidelines -- Please add your answers below. If you do not know the answer, please add “Open to suggestions from the community/looking for creative solutions”',
description: '',
type: 'questions',
questions: [
@@ -135,7 +135,7 @@ const sections = [
hideTitle: false,
title: 'User Roles',
type: 'questions',
- description: 'Please place an X in the Required column for each user type/role. Please provide details on what the user/role should do in the Description column.',
+ description: 'Please select each for each user type/role. Please provide details on what the user/role should do in the Description column.',
questions: [
{
// required is not needed if we specifiy validations
@@ -200,7 +200,7 @@ const sections = [
id: 'screen-features',
required: false,
hideTitle: false,
- title: 'Screen and Features',
+ title: 'Screens and Features',
description: '',
type: 'questions',
questions: [
@@ -339,12 +339,12 @@ const sections = [
{ value: 'auditing', label: 'Audit - Auditing will record user information on actions performed.' },
{ value: 'confidential', label: 'System will be working with confidential, health or financial records'}
],
- description: 'Please place an X in the Required column for each required security requirement.',
+ description: 'Please select each for each required security requirement.',
},
{
icon: 'question',
title: 'Quality Assurance, Test Data & Performance Testing',
- description: 'Please place an X in the Required column for each required QA requirement.',
+ description: 'Please select each for each required QA requirement.',
fieldName: 'details.qaTesting.testing',
type: 'checkbox-group',
options: [
@@ -378,7 +378,7 @@ const sections = [
{
icon: 'question',
title: 'User Acceptance / Beta Testing',
- description: 'UAT is the process of sharing the final application with users and gathering feedback. Please place an X in the Required column for each required UAT requirement.',
+ description: 'UAT is the process of sharing the final application with users and gathering feedback. Please select each for each required UAT requirement.',
fieldName: 'details.qaTesting.uat',
type: 'checkbox-group',
options: [
@@ -398,8 +398,7 @@ const sections = [
questions: [
{
icon: 'question',
- description: 'How much budget do you have? Please place an X in the Confirm column to specify your budget.',
-
+ description: 'How much budget do you have?',
title: 'Budget',
fieldName: 'details.loadDetails.budget',
type: 'slide-radiogroup',
@@ -415,7 +414,7 @@ const sections = [
},
{
icon: 'question',
- description: 'When do you need your app by? Please place an X in the Confirm column to specify your timeline.',
+ description: 'When do you need your app by? ',
title: 'Timeline',
fieldName: 'details.loadDetails.timeline',
type: 'slide-radiogroup',
@@ -487,7 +486,7 @@ export const basicSections = [
validationError: 'Please let us know the target device',
title: 'App Type',
description: 'What type of application are we developing? Please \
- place an X in the Required column for each required app \
+ the required app \
type. Please note that each additional app type incurs \
a cost, but that the cost will be detailed and broken \
out in the final project proposal. ',
@@ -519,7 +518,7 @@ export const basicSections = [
{
icon: 'question',
title: 'Form Factor/Orientation',
- description: 'Please place an X in the Required column for each \
+ description: 'Please select each for each \
form factor/orientation that must be supported.',
fieldName: 'details.appDefinition.formFactor',
type: 'checkbox-group',
@@ -536,7 +535,7 @@ export const basicSections = [
{
required: false,
hideTitle: false,
- title: 'Style Guide & Brand Guidelines',
+ title: 'Style Guide & Brand Guidelines - Please add your answers below. If you do not know the answer, please add “Open to suggestions from the community/looking for creative solutions”',
description: '',
type: 'questions',
questions: [
@@ -577,9 +576,9 @@ export const basicSections = [
{
required: false,
hideTitle: false,
- title: 'User Roles',
+ title: 'User Roles - Please use the fields below to specify the type of users/roles for the application. If the role is not applicable, please enter N/A',
type: 'questions',
- description: 'Please place an X in the Required column for each user type/role. Please provide details on what the user/role should do in the Description column.',
+ description: 'Please select each for each user type/role. Please provide details on what the user/role should do in the Description column.',
questions: [
{
// required is not needed if we specifiy validations
@@ -610,7 +609,7 @@ export const basicSections = [
{
required: false,
hideTitle: false,
- title: 'Integrations',
+ title: ' - Will this application be dependant on data from another system or tool? If yes, please briefly describe that dependency here. This can include integration with an API or an existing backend/database.',
description: 'Will this application be dependant on data from another system or tool? If yes, please briefly describe that dependency here. This can include integration with an API or an existing backend/database.',
type: 'questions',
questions: [
@@ -651,9 +650,7 @@ export const basicSections = [
{
icon: 'question',
title: 'Screen / Feature List',
- description: 'Please note that each added feature incurs a cost, \
- but that the cost will be detailed and broken out in the \
- final project proposal. ',
+ description: 'Please select each required feature. Please note that each added feature incurs a cost, but that the cost will be detailed and broken out in the final project proposal. ',
fieldName: 'details.appDefinition.screens',
type: 'checkbox-group',
options: [
@@ -691,8 +688,8 @@ export const basicSections = [
{
icon: 'question',
- title: 'Tech Features',
- description: '',
+ title: 'Technology Requirements',
+ description: 'Please select each required technology requirement above',
fieldName: 'details.appDefinition.techFeatures',
type: 'checkbox-group',
options: [
@@ -719,7 +716,7 @@ export const basicSections = [
id: 'techStack',
required: false,
hideTitle: false,
- title: 'Technology Stack',
+ title: 'Technology Stack - Do you have a preferred technology stack? If yes, please list those requirements here:',
description: 'Do you have a preferred technology stack? If yes, please list those requirements here:',
type: 'questions',
questions: [
@@ -776,29 +773,36 @@ export const basicSections = [
title: 'Security Requirements',
type: 'checkbox-group',
options: [
- { value: 'standard', label: 'Standard Security' },
- { value: 'enterprise', label: 'Enterprise - if your application will house\
- or transmit PII or sensitive data. The data will be encrypted on the device and the server.' },
- { value: 'vulnerability', label: 'Vulnerability Scanning - Scan your application for weaknesses' },
- { value: 'auditing', label: 'Audit - Auditing will record user information on actions performed.' },
- { value: 'confidential', label: 'System will be working with confidential, health or financial records'}
+ { value: 'standard', label: 'Standard Security - Select this option if your app requires standard security.' },
+ { value: 'enterprise', label: 'Enterprise - Select this option if your application will house or transmit PII or sensitive data. The data will be encrypted on the device and the server.' },
+ { value: 'vulnerability', label: 'Vulnerability Scanning - Vulnerability scanning is a security technique used to identify security weaknesses in a computer system.' },
+ { value: 'auditing', label: 'Audit - Is it necessary to audit user actions? Auditing will keep a record of specific user actions like data creation/modification and will be helpful in identifying which user performed a particular action.' },
+ { value: 'confidential', label: 'Confidential Information, Sensitive Financial Data or Personally Identifiable Information (PII) - Will the user be working directly with financial or other protected information such has health records?'},
+ { value: 'mdm', label: 'Mobile Device Management (MDM) - Do you employ an MDM solution? If yes, what service do you use?'}
],
- description: 'Please place an X in the Required column for each required security requirement.',
+ description: 'Please select each required security requirement above.',
},
{
icon: 'question',
title: 'Quality Assurance, Test Data & Performance Testing',
- description: 'Please place an X in the Required column for each required QA requirement.',
+ description: 'Please select each for each required QA requirement.',
fieldName: 'details.qaTesting.testing',
type: 'checkbox-group',
options: [
- { value: 'rw-unstructured', label: 'Real World Unstructured - Users search on their own for bugs or usability issues.' },
- { value: 'rw-structured', label: 'Structured Functional - execution of predefined test scripts' },
- { value: 'testcases', label: 'Test Case Creation - creation of scenarios, instructions and exepected results' },
- { value: 'certification', label: 'Certify your mobile application release against predefined device set including.' },
- { value: 'devicelab', label: 'Test real devices in real cell networks across the world' },
- { value: 'performanceTuning', label: 'Identify and provide perfromance improvements' },
- { value: 'performanceTesting', label: 'Testing web application robustness' },
+ { value: 'rw-unstructured', label: 'Real World Unstructured - Functional testing performed without test scripts. Users search on their own for bugs or usability issues.' },
+ { value: 'rw-structured', label: 'Real World Testing - Structured - Test case based execution, covering all the functional requirements & cross-browser device testing.' },
+ { value: 'testcases', label: 'Test Cases/Scenarios - \
+Creation of test cases/test scenarios including scenario setup, pre/post conditions to scenario, instructions to execute scenario, and expected results' },
+ { value: 'certification', label: 'App Certification - Certify your mobile application release against predefined device set including; \
+\
+ --App profiling to see the device vital monitoring – CPU, battery and memory usage of APP;\
+\
+ --App behavior analysis in different modes (inactive, active, low battery, );\
+\
+ --App performance under various interrupts, under simulated network conditions, etc. ' },
+ { value: 'devicelab', label: 'Mobile Device Lab on Hire - Allows you to remotely access devices in real cell networks across the world' },
+ { value: 'performanceTuning', label: 'Performance Testing - Testing web application’s robustness, availability, and reliability for defined business scenarios and concurrent users.' },
+ { value: 'performanceTesting', label: 'Performance Tuning - Analyze and identify performance issues, actionable items for improvement.' },
]
},
{
@@ -822,7 +826,7 @@ export const basicSections = [
{
icon: 'question',
title: 'User Acceptance / Beta Testing',
- description: 'UAT is the process of sharing the final application with users and gathering feedback. Please place an X in the Required column for each required UAT requirement.',
+ description: 'UAT is the process of sharing the final application with users and gathering feedback. Please select each required UAT requirement.',
fieldName: 'details.qaTesting.uat',
type: 'checkbox-group',
options: [
@@ -842,8 +846,7 @@ export const basicSections = [
questions: [
{
icon: 'question',
- description: 'How much budget do you have? Please place an X in the Confirm column to specify your budget.',
-
+ description: 'How much budget do you have?',
title: 'Budget',
fieldName: 'details.loadDetails.budget',
type: 'slide-radiogroup',
@@ -859,7 +862,7 @@ export const basicSections = [
},
{
icon: 'question',
- description: 'When do you need your app by? Please place an X in the Confirm column to specify your timeline.',
+ description: 'When do you need your app by?',
title: 'Timeline',
fieldName: 'details.loadDetails.timeline',
type: 'slide-radiogroup',
@@ -883,4 +886,4 @@ export const basicSections = [
}
]
}
-]
\ No newline at end of file
+]
diff --git a/src/projects/list/components/Projects/ProjectListNavHeader.jsx b/src/projects/list/components/Projects/ProjectListNavHeader.jsx
index 95ccfca3d..8b785ac64 100644
--- a/src/projects/list/components/Projects/ProjectListNavHeader.jsx
+++ b/src/projects/list/components/Projects/ProjectListNavHeader.jsx
@@ -87,6 +87,16 @@ export default class ProjectListNavHeader extends Component {
)
)}
+
diff --git a/src/projects/list/components/Projects/ProjectListNavHeader.scss b/src/projects/list/components/Projects/ProjectListNavHeader.scss
index deba0c2e1..7b5dad2ad 100644
--- a/src/projects/list/components/Projects/ProjectListNavHeader.scss
+++ b/src/projects/list/components/Projects/ProjectListNavHeader.scss
@@ -16,11 +16,20 @@
}
@media screen and (max-width: $screen-sm - 1px) {
- margin: 0 0 4 * $base-unit 0;
+ margin: 0 0 $base-unit 0;
}
.left-wrapper {
flex: 1 1 auto;
+ .primary-filter {
+ position: absolute;
+ top: 60px;
+ right: 0;
+
+ @media screen and (min-width: $screen-sm) {
+ display: none;
+ }
+ }
}
.right-wrapper {
@@ -59,10 +68,6 @@
.primary-filter {
margin-right: 20px;
- @media screen and (max-width: $screen-md - 1px) {
- display: none;
- }
-
.tc-switch .label {
white-space: nowrap;
}
diff --git a/src/projects/list/components/Projects/ProjectsGridView.scss b/src/projects/list/components/Projects/ProjectsGridView.scss
index 26d455850..50460b9ef 100644
--- a/src/projects/list/components/Projects/ProjectsGridView.scss
+++ b/src/projects/list/components/Projects/ProjectsGridView.scss
@@ -191,7 +191,7 @@ $screen-one-column: 720px;
font-size: 13px;
align-items: center;
margin-top: $base-unit;
- margin-right: 40px;
+ margin-right: $base-unit;
> div {
overflow: hidden;
diff --git a/src/projects/list/components/Walkthrough/Walkthrough.jsx b/src/projects/list/components/Walkthrough/Walkthrough.jsx
index 665cca824..433f4aaef 100644
--- a/src/projects/list/components/Walkthrough/Walkthrough.jsx
+++ b/src/projects/list/components/Walkthrough/Walkthrough.jsx
@@ -1,9 +1,11 @@
import React from 'react'
import PropTypes from 'prop-types'
+import {Link} from 'react-router-dom'
import './Walkthrough.scss'
import IconShadow from '../../../../assets/icons/ground-shadow.svg'
import IconRobot from '../../../../assets/icons/coder-welcome.svg'
import IconTextImg from '../../../../assets/icons/pointer-new-project.svg'
+import BoldAdd from '../../../../assets/icons/ui-16px-1_bold-add.svg'
@@ -13,13 +15,22 @@ const Walkthrough = ({currentUser}) => (
-
-
+
010010010010100101001000100100101
Bzzt …I mean… Hello, {currentUser.firstName}!
Welcome to Connect! I’m Coder the Robot. I see you have no projects yet. To get you started, press the “New Project” icon and let’s build something.
+
+
+
+
+
+
Create a new project
+
+
+
+
)
diff --git a/src/projects/list/components/Walkthrough/Walkthrough.scss b/src/projects/list/components/Walkthrough/Walkthrough.scss
index 76bbdec8b..984751434 100644
--- a/src/projects/list/components/Walkthrough/Walkthrough.scss
+++ b/src/projects/list/components/Walkthrough/Walkthrough.scss
@@ -6,21 +6,29 @@
.walkthrough-column {
position: relative;
text-align: center;
- min-width: 768px;
min-height: 496px;
- padding: 20px 86px;
+ padding: 20px 56px;
+ @media screen and (max-width: $screen-sm - 1px) {
+ padding: 20px 26px;
+ }
.text-img{
background-size: 100% 100%;
- position: absolute;
- display:block;
- width: 640px;
- height: 160px;
- right: 80px;
- z-index:2;
+ position: relative;
+ display: block;
+ width: 90%;
+ left: 10%;
+ @media screen and (max-width: $screen-sm - 1px) {
+ display: none;
+ }
+ @media screen and (min-width: $screen-md - 1px) {
+ width: 60%;
+ left: 40%;
+ }
}
h3{
+ word-break: break-all;
color: $tc-gray-70;
@include roboto-medium;
font-size: 48px;
@@ -60,19 +68,77 @@
}
.bubble{
- position: absolute;
- top: 180px;
- left: calc(50% - 300px);
- padding-left:243px;
-
+ position: relative;
+ padding:0 10%;
+ display: flex;
+ padding: 0 20% 0 20%;
+ @media screen and (max-width: $screen-sm - 1px) {
+ left:0;
+ padding-left: 0;
+ display: block;
+ margin: 0;
+ }
+ @media screen and (max-width: 1200px) {
+ padding: 0 10% 0 10%;
+ }
+ @media screen and (max-width: 1000px) {
+ padding: 0;
+ }
div{
position:relative;
background: $tc-white;
- width: 436px;
min-height: 235px;
padding: 30px 30px 50px 30px;
box-shadow: 0 0 15px rgba($tc-black, .15);
border-radius: 4px;
+ @media screen and (max-width: $screen-sm - 1px) {
+ width: auto;
+ padding: 30px 30px 30px 30px;
+ }
+ a {
+ display: none;
+ @media screen and (max-width: $screen-sm - 1px) {
+ display: initial;
+ }
+ .new-project-button {
+ @include roboto;
+ color: #4285f4;
+ margin-top: 30px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ .title {
+ white-space: nowrap;
+ margin-left: 10px;
+ }
+ .new-project-icon {
+ width: 30px;
+ height: 30px;
+ background-color: rgb(66, 133, 244);
+ border-radius: 100%;
+ display: -webkit-box;
+ display: -ms-flexbox;
+ display: flex;
+ -webkit-box-pack: center;
+ -ms-flex-pack: center;
+ justify-content: center;
+ -webkit-box-align: center;
+ -ms-flex-align: center;
+ align-items: center;
+ svg {
+ fill: #fff;
+ width: 10px;
+ height: 10px;
+ }
+ }
+ }
+ div {
+ box-shadow: none;
+ min-height: 0;
+ padding: 0;
+ width: auto;
+ }
+ }
h3{
@include roboto-medium;
line-height:35px;
@@ -80,6 +146,13 @@
color: $tc-black;
text-align:left;
padding-bottom: 10px;
+ @media screen and (max-width: $screen-md - 1px) {
+ font-size: 18px;
+ }
+ @media screen and (max-width: $screen-sm - 1px) {
+ font-size: 16px;
+ line-height:30px;
+ }
}
p{
@include roboto;
@@ -87,6 +160,12 @@
color: $tc-gray-80;
line-height: 25px;
padding: 0;
+ @media screen and (max-width: $screen-md - 1px) {
+ font-size: 14px;
+ }
+ @media screen and (max-width: $screen-sm - 1px) {
+ font-size: 13px;
+ }
}
.arrow{
width:0;
@@ -96,28 +175,56 @@
top: 42px;
border-left:34px solid transparent;
border-top:34px solid $tc-white;
+ @media screen and (max-width: $screen-sm - 1px) {
+ border-left: none;
+ border-right:34px solid transparent;
+ bottom: -20px;
+ left: inherit;
+ top: inherit;
+ }
}
}
}
.robot{
background-size: 238px 281px;
- position: absolute;
width: 238px;
height: 281px;
- left:0;
- display:block;
+ left: 30%;
top:0;
z-index:2;
+ display: none;
+ &.robotleft {
+ width: 100%;
+ display: block;
+ @media screen and (max-width: $screen-sm - 1px) {
+ display: none;
+ }
+ }
+ @media screen and (max-width: $screen-sm - 1px) {
+ width: 100%;
+ position: relative;
+ margin: 50px 0;
+ left: 0%;
+ display: block;
+ }
+ ellipse {
+ display: none;
+ @media screen and (max-width: $screen-sm - 1px) {
+ display: initial;
+ }
+ }
}
.shadow{
background-size: 960px 11px;
position: absolute;
- width: 960px;
+ width: 100%;
z-index:1;
display:block;
height: 11px;
- left:-150px;
bottom:-48px;
+ @media screen and (max-width: $screen-sm - 1px) {
+ display: none;
+ }
}
p{
text-align: left;
diff --git a/src/routes/settings/components/SettingsPanel.scss b/src/routes/settings/components/SettingsPanel.scss
index 0a9481a41..3ea21a9e4 100644
--- a/src/routes/settings/components/SettingsPanel.scss
+++ b/src/routes/settings/components/SettingsPanel.scss
@@ -5,11 +5,9 @@
.settings-panel {
margin: 20px auto 0;
padding: 0 20px;
- min-width: 960px;
@media screen and (max-width: $screen-md - 1px) {
margin: 0;
- min-width: 0;
padding: 0;
}
diff --git a/src/routes/settings/routes.jsx b/src/routes/settings/routes.jsx
index 95b3efe56..900456a6e 100644
--- a/src/routes/settings/routes.jsx
+++ b/src/routes/settings/routes.jsx
@@ -2,7 +2,7 @@
* Settings routes
*/
import React from 'react'
-import { Route, Switch } from 'react-router-dom'
+import { Route } from 'react-router-dom'
import { renderApp } from '../../components/App/App'
import TopBarContainer from '../../components/TopBar/TopBarContainer'
import NotificationsToolBar from '../notifications/components/NotificationsToolBar'
@@ -12,9 +12,7 @@ import NotificationSettingsContainer from './routes/notifications/containers/Not
// import ProfileSettingsContainer from './routes/profile/containers/ProfileSettingsContainer'
export default (
-
- {/*, )} />*/}
- {/*, )} />*/}
- , )} />
-
+ {/*
,
)} />*/},
+ {/*
,
)} />*/},
+
,
)} />
)
diff --git a/src/routes/settings/routes/notifications/components/NotificationSettingsForm.jsx b/src/routes/settings/routes/notifications/components/NotificationSettingsForm.jsx
index 8446f5c66..8ad2c1abf 100644
--- a/src/routes/settings/routes/notifications/components/NotificationSettingsForm.jsx
+++ b/src/routes/settings/routes/notifications/components/NotificationSettingsForm.jsx
@@ -5,9 +5,13 @@ import React from 'react'
import PropTypes from 'prop-types'
import FormsyForm from 'appirio-tech-react-components/components/Formsy'
const Formsy = FormsyForm.Formsy
+import { NOTIFICATION_SETTINGS_PERIODS } from '../../../../../config/constants'
import SwitchButton from 'appirio-tech-react-components/components/SwitchButton/SwitchButton'
+import Tooltip from 'appirio-tech-react-components/components/Tooltip/Tooltip'
+import { TOOLTIP_DEFAULT_DELAY } from '../../../../../config/constants'
import BtnGroup from '../../../../../components/BtnGroup/BtnGroup'
import IconSettingsWeb from '../../../../../assets/icons/settings-icon-web.svg'
+import IconSettingsEmail from '../../../../../assets/icons/settings-icon-mail.svg'
import './NotificationSettingsForm.scss'
import _ from 'lodash'
@@ -17,6 +21,7 @@ import _ from 'lodash'
const topics = [
{
title: 'New posts and replies',
+ enabledMethods:['web', 'email'],
types: [
'notifications.connect.project.topic.created',
'notifications.connect.project.topic.deleted',
@@ -26,6 +31,7 @@ const topics = [
]
}, {
title: 'Project status changes',
+ enabledMethods:['web'],
types: [
'notifications.connect.project.created',
'notifications.connect.project.updated',
@@ -38,21 +44,25 @@ const topics = [
]
}, {
title: 'Project specification changes',
+ enabledMethods:['web'],
types: [
'notifications.connect.project.specificationModified'
]
}, {
title: 'File uploads',
+ enabledMethods:['web'],
types: [
'notifications.connect.project.fileUploaded'
]
}, {
title: 'New project links',
+ enabledMethods:['web'],
types: [
'notifications.connect.project.linkCreated'
]
}, {
title: 'Team changes',
+ enabledMethods:['web'],
types: [
'notifications.connect.project.member.joined',
'notifications.connect.project.member.left',
@@ -78,22 +88,28 @@ const topics = [
*/
const initSettings = (notInitedSettings) => {
const settings = {...notInitedSettings}
+ const notifications = {...settings.notifications}
const allTypes = _.flatten(_.map(topics, 'types'))
allTypes.forEach((type) => {
- if (!settings[type]) {
- settings[type] = {}
+ if (!notifications[type]) {
+ notifications[type] = {}
}
- // check each of deliveryMethod method separately as some can have
+ // check each of serviceId method separately as some can have
// values and some don't have
- ['web', 'email'].forEach((deliveryMethod) => {
- if (_.isUndefined(settings[type][deliveryMethod])) {
- settings[type][deliveryMethod] = 'yes'
+ ['web', 'email'].forEach((serviceId) => {
+ if (!notifications[type][serviceId]) {
+ notifications[type][serviceId] = {}
+ }
+ if (_.isUndefined(notifications[type][serviceId].enabled)) {
+ notifications[type][serviceId].enabled = 'yes'
}
})
})
+ settings.notifications = notifications
+
return settings
}
@@ -107,26 +123,45 @@ class NotificationSettingsForm extends React.Component {
}
this.handleChange = this.handleChange.bind(this)
+ this.handleBundleEmailChange = this.handleBundleEmailChange.bind(this)
}
- handleChange(topicIndex, deliveryMethod) {
- const s = {
- settings: {
- ...this.state.settings
- }
- }
+ handleChange(topicIndex, serviceId) {
+ const notifications = {...this.state.settings.notifications}
// update values for all types of the topic
topics[topicIndex].types.forEach((type) => {
- s.settings[type][deliveryMethod] = s.settings[type][deliveryMethod] === 'yes' ? 'no' : 'yes'
+ notifications[type][serviceId].enabled = notifications[type][serviceId].enabled === 'yes' ? 'no' : 'yes'
+ })
+
+ this.setState({
+ settings: {
+ ...this.state.settings,
+ notifications,
+ }
})
+ }
- this.setState(s)
+ handleBundleEmailChange(bundlePeriod) {
+ this.setState({
+ settings: {
+ ...this.state.settings,
+ services: {
+ ...this.state.settings.services,
+ email: {
+ ...this.state.settings.services.email,
+ // this will be send to backend which uses null instead of 'immediately'
+ bundlePeriod: bundlePeriod === 'immediately' ? null : bundlePeriod,
+ }
+ }
+ }
+ })
}
render() {
const areSettingsProvided = !!this.props.values.settings
const settings = this.state.settings
+ const notifications = settings.notifications
// if settings weren't provided (not loaded) don't render anything
if (!areSettingsProvided) {
@@ -145,8 +180,9 @@ class NotificationSettingsForm extends React.Component {
Web |
- {/* as email notification currently not supported, hide them for now */}
- {/*
Email | */}
+
+
+ Email |
@@ -154,40 +190,45 @@ class NotificationSettingsForm extends React.Component {
// we toggle settings for all the types in one topic all together
// so we can use values from the first type to get current value for the whole topic
const topicFirstType = topic.types[0]
+ const emailStatus = topic.enabledMethods.indexOf('email') < 0 ? 'disabled' : null
+ const emailTooltip = topic.enabledMethods.indexOf('email') < 0 ? 'Emails are not yet supported for this event type' : null
return (
| {topic.title} |
- this.handleChange(index, 'web')} defaultChecked={settings[topicFirstType] && settings[topicFirstType].web === 'yes'} /> |
- {/* as email notification currently not supported, hide them for now */}
- {/* this.handleChange(topic, 'email')} defaultChecked={settings[topic] && settings[topic].email === 'yes'} /> | */}
+ this.handleChange(index, 'web')} defaultChecked={notifications[topicFirstType].web.enabled === 'yes'} /> |
+
+ { !!emailTooltip &&
+
+
+ this.handleChange(index, 'email')} defaultChecked={notifications[topicFirstType].email.enabled === 'yes' && emailStatus===null} disabled={emailStatus}/>
+
+
+ {emailTooltip}
+
+
+ }
+ {
+ !emailTooltip && this.handleChange(index, 'email')} defaultChecked={notifications[topicFirstType].email.enabled === 'yes' && emailStatus===null} disabled={emailStatus}/>
+ }
+ |
)
})}
-
- { false &&
+
- Bundle emails:
+ Bundle emails (beta):
|
- }
-
-
diff --git a/src/routes/settings/routes/notifications/components/NotificationSettingsForm.scss b/src/routes/settings/routes/notifications/components/NotificationSettingsForm.scss
index af606e84c..7962c54d7 100644
--- a/src/routes/settings/routes/notifications/components/NotificationSettingsForm.scss
+++ b/src/routes/settings/routes/notifications/components/NotificationSettingsForm.scss
@@ -104,6 +104,14 @@
margin-left: 10px;
margin-bottom: 10px;
}
+
+ @media screen and (max-width: 430px - 1px) {
+ > span {
+ display: block;
+ margin-left: $base-unit;
+ margin-right: $base-unit;
+ }
+ }
}
.none {
@@ -135,21 +143,5 @@
margin-top: 6 * $base-unit;
text-align: center;
}
-
- > .email-settings {
- @include roboto;
- margin: 20px 0;
-
- // Link colors
- a:link,
- a:visited {
- color: $tc-dark-blue;
- }
-
- a:hover,
- a:active {
- color: $tc-dark-blue-70;
- }
- }
}
}
diff --git a/src/routes/settings/routes/notifications/containers/NotificationSettingsContainer.jsx b/src/routes/settings/routes/notifications/containers/NotificationSettingsContainer.jsx
index 4edb1a655..93fd120b7 100644
--- a/src/routes/settings/routes/notifications/containers/NotificationSettingsContainer.jsx
+++ b/src/routes/settings/routes/notifications/containers/NotificationSettingsContainer.jsx
@@ -25,10 +25,6 @@ class NotificationSettingsContainer extends React.Component {
return (
diff --git a/src/routes/settings/services/settings.js b/src/routes/settings/services/settings.js
index 5f62423a3..52295aea5 100644
--- a/src/routes/settings/services/settings.js
+++ b/src/routes/settings/services/settings.js
@@ -3,7 +3,6 @@
*
* TODO has to be replaced with the real service
*/
-import _ from 'lodash'
import { axiosInstance as axios } from '../../../api/requestInterceptor'
import { TC_NOTIFICATION_URL } from '../../../config/constants'
@@ -66,42 +65,8 @@ const getNotificationSettings = () => {
.then(resp => resp.data)
}
-// TODO move this list to constants together with the same list in NotificationSettingsForm.jsx
-const topics = [
- 'notifications.connect.project.created',
- 'notifications.connect.project.updated',
- 'notifications.connect.project.canceled',
- 'notifications.connect.project.approved',
- 'notifications.connect.project.paused',
- 'notifications.connect.project.completed',
- 'notifications.connect.project.submittedForReview',
- 'notifications.connect.project.active',
-
- 'notifications.connect.project.fileUploaded',
- 'notifications.connect.project.specificationModified',
- 'notifications.connect.project.linkCreated',
-
- 'notifications.connect.project.member.joined',
- 'notifications.connect.project.member.left',
- 'notifications.connect.project.member.removed',
- 'notifications.connect.project.member.managerJoined',
- 'notifications.connect.project.member.copilotJoined',
- 'notifications.connect.project.member.assignedAsOwner',
-
- 'notifications.connect.project.topic.created',
- 'notifications.connect.project.topic.deleted',
- 'notifications.connect.project.post.created',
- 'notifications.connect.project.post.edited',
- 'notifications.connect.project.post.deleted'
-]
-
const saveNotificationSettings = (data) => {
- const body = []
- _.each(topics, (topic) => {
- body.push({ topic, deliveryMethod: 'email', value: data[topic] && data[topic].email === 'yes' ? 'yes' : 'no' })
- body.push({ topic, deliveryMethod: 'web', value: data[topic] && data[topic].web === 'yes' ? 'yes' : 'no' })
- })
- return axios.put(`${TC_NOTIFICATION_URL}/settings`, body)
+ return axios.put(`${TC_NOTIFICATION_URL}/settings`, data)
}
export default {