Skip to content
This repository was archived by the owner on Mar 13, 2025. It is now read-only.

Commit bfdc589

Browse files
authored
Merge pull request #19 from MadOPcode/feature/work-periods-part-2
Implemented working period details
2 parents 7e53e8e + 5e6c585 commit bfdc589

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+3051
-227
lines changed

config/dev.js

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
module.exports = {
22
API: {
3+
V3: "https://api.topcoder-dev.com/v3",
34
V5: "https://api.topcoder-dev.com/v5",
45
},
56
PLATFORM_WEBSITE_URL: "https://platform.topcoder-dev.com",
7+
TOPCODER_WEBSITE_URL: "https://www.topcoder-dev.com",
68
};

config/index.js

-4
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,5 @@
22

33
module.exports = (() => {
44
const appEnv = process.env.APPENV === "prod" ? "prod" : "dev";
5-
6-
// eslint-disable-next-line no-console
7-
console.log(`APPENV: "${appEnv}"`);
8-
95
return require(`./${appEnv}`);
106
})();

config/prod.js

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
module.exports = {
22
API: {
3+
V3: "https://api.topcoder.com/v3",
34
V5: "https://api.topcoder.com/v5",
45
},
56
PLATFORM_WEBSITE_URL: "https://platform.topcoder.com",
7+
TOPCODER_WEBSITE_URL: "https://www.topcoder.com",
68
};

src/assets/images/icon-computer.svg

+20
Loading

src/assets/images/icon-copy.png

500 Bytes
Loading
2.7 KB
Loading

src/assets/images/icon-link.png

593 Bytes
Loading
363 Bytes
Loading

src/components/Button/index.jsx

+4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import styles from "./styles.module.scss";
1010
* @param {Object} props.children button text
1111
* @param {string} [props.className] class name added to root element
1212
* @param {'primary'|'primary-dark'|'primary-light'} [props.color] button color
13+
* @param {boolean} [props.isDisabled] if button is disabled
1314
* @param {boolean} [props.isSelected] if button is selected
1415
* @param {string} [props.name] button name
1516
* @param {(e: any) => void} props.onClick function called when button is clicked
@@ -24,6 +25,7 @@ const Button = ({
2425
children,
2526
className,
2627
color = "primary",
28+
isDisabled = false,
2729
isSelected = false,
2830
name,
2931
onClick,
@@ -35,6 +37,7 @@ const Button = ({
3537
}) => (
3638
<button
3739
data-value={value}
40+
disabled={isDisabled}
3841
name={name || ""}
3942
type={type}
4043
className={cn(
@@ -58,6 +61,7 @@ Button.propTypes = {
5861
children: PT.node,
5962
className: PT.string,
6063
color: PT.oneOf(["primary"]),
64+
isDisabled: PT.bool,
6165
isSelected: PT.bool,
6266
name: PT.string,
6367
onClick: PT.func,

src/components/Button/styles.module.scss

+17
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
text-transform: uppercase;
1111
outline: none;
1212
cursor: pointer;
13+
14+
&:disabled {
15+
opacity: 1;
16+
cursor: not-allowed;
17+
}
1318
}
1419

1520
.medium {
@@ -55,6 +60,12 @@
5560
border-color: $primary-dark-color;
5661
color: $primary-dark-text-color;
5762
}
63+
64+
&:disabled {
65+
border-color: $control-disabled-border-color;
66+
background-color: $control-disabled-bg-color;
67+
color: $control-disabled-text-color;
68+
}
5869
}
5970

6071
.contained {
@@ -76,6 +87,12 @@
7687
border-color: $primary-dark-color;
7788
background-color: $primary-dark-color;
7889
}
90+
91+
&:disabled {
92+
border-color: $control-disabled-border-color;
93+
background-color: $control-disabled-bg-color;
94+
color: $control-disabled-text-color;
95+
}
7996
}
8097

8198
.circle,

src/components/Checkbox/index.jsx

+10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from "react";
22
import PT from "prop-types";
33
import cn from "classnames";
4+
import { stopPropagation } from "utils/misc";
45
import styles from "./styles.module.scss";
56

67
/**
@@ -9,30 +10,37 @@ import styles from "./styles.module.scss";
910
* @param {Object} props component properties
1011
* @param {boolean} props.checked whether checkbox is checked
1112
* @param {string} [props.className] class name added to root element
13+
* @param {boolean} [props.isDisabled] if checkbox is disabled
1214
* @param {string} props.name name for input element
1315
* @param {() => void} props.onChange function called when checkbox changes state
1416
* @param {Object} [props.option] object { value, label }
1517
* @param {'medium'|'small'} [props.size] checkbox size
18+
* @param {boolean} [props.stopClickPropagation] whether to stop click event propagation
1619
* @returns {JSX.Element}
1720
*/
1821
const Checkbox = ({
1922
checked,
2023
className,
24+
isDisabled = false,
2125
name,
2226
onChange,
2327
option,
2428
size = "medium",
29+
stopClickPropagation = false,
2530
}) => (
31+
// eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions
2632
<label
2733
className={cn(
2834
styles.container,
2935
styles[size],
3036
{ [styles.single]: !option || !option.label },
3137
className
3238
)}
39+
onClick={stopClickPropagation ? stopPropagation : null}
3340
>
3441
<input
3542
type="checkbox"
43+
disabled={isDisabled}
3644
className={styles.checkbox}
3745
name={name}
3846
onChange={onChange}
@@ -49,13 +57,15 @@ const Checkbox = ({
4957
Checkbox.propTypes = {
5058
checked: PT.bool,
5159
className: PT.string,
60+
isDisabled: PT.bool,
5261
name: PT.string.isRequired,
5362
size: PT.oneOf(["medium", "small"]),
5463
onChange: PT.func.isRequired,
5564
option: PT.shape({
5665
value: PT.string.isRequired,
5766
label: PT.string,
5867
}),
68+
stopClickPropagation: PT.bool,
5969
};
6070

6171
export default Checkbox;

src/components/Checkbox/styles.module.scss

+7
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,13 @@ input.checkbox {
3636
}
3737
}
3838
}
39+
40+
&:disabled {
41+
+ .impostor {
42+
background-color: $control-disabled-bg-color;
43+
cursor: not-allowed;
44+
}
45+
}
3946
}
4047

4148
.impostor {

src/components/IntegerField/index.jsx

+22-3
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import styles from "./styles.module.scss";
88
*
99
* @param {Object} props component properties
1010
* @param {string} [props.className] class name to be added to root element
11+
* @param {boolean} [props.isDisabled] if the field is disabled
1112
* @param {string} props.name field's name
1213
* @param {number} props.value field's value
1314
* @param {number} [props.maxValue] maximum allowed value
@@ -17,27 +18,45 @@ import styles from "./styles.module.scss";
1718
*/
1819
const IntegerField = ({
1920
className,
21+
isDisabled = false,
2022
name,
2123
onChange,
2224
value,
2325
maxValue = Infinity,
2426
minValue = -Infinity,
2527
}) => (
2628
<div className={cn(styles.container, className)}>
29+
<input
30+
disabled={isDisabled}
31+
readOnly
32+
className={styles.input}
33+
name={name}
34+
value={value}
35+
/>
2736
<button
2837
className={styles.btnMinus}
29-
onClick={() => onChange(Math.max(value - 1, minValue))}
38+
onClick={(event) => {
39+
event.stopPropagation();
40+
if (!isDisabled) {
41+
onChange(Math.max(value - 1, minValue));
42+
}
43+
}}
3044
/>
3145
<button
3246
className={styles.btnPlus}
33-
onClick={() => onChange(Math.min(+value + 1, maxValue))}
47+
onClick={(event) => {
48+
event.stopPropagation();
49+
if (!isDisabled) {
50+
onChange(Math.min(+value + 1, maxValue));
51+
}
52+
}}
3453
/>
35-
<input readOnly className={styles.input} name={name} value={value} />
3654
</div>
3755
);
3856

3957
IntegerField.propTypes = {
4058
className: PT.string,
59+
isDisabled: PT.bool,
4160
name: PT.string.isRequired,
4261
maxValue: PT.number,
4362
minValue: PT.number,

src/components/IntegerField/styles.module.scss

+12
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ input.input {
1919
outline: none !important;
2020
box-shadow: none !important;
2121
text-align: center;
22+
23+
&:disabled {
24+
border-color: $control-disabled-border-color;
25+
background-color: $control-disabled-bg-color;
26+
color: $control-disabled-text-color;
27+
cursor: not-allowed;
28+
29+
~ .btnMinus,
30+
~ .btnPlus {
31+
cursor: not-allowed;
32+
}
33+
}
2234
}
2335

2436
.btnMinus,

src/components/Page/index.jsx

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import React from "react";
22
import PT from "prop-types";
33
import cn from "classnames";
4+
import ReduxToastr from "react-redux-toastr";
45
import styles from "./styles.module.scss";
56

67
/**
@@ -12,7 +13,15 @@ import styles from "./styles.module.scss";
1213
* @returns {Object}
1314
*/
1415
const Page = ({ className, children }) => (
15-
<div className={cn(styles.container, className)}>{children}</div>
16+
<div className={cn(styles.container, className)}>
17+
{children}
18+
<ReduxToastr
19+
timeOut={60000}
20+
position="top-right"
21+
transitionIn="fadeIn"
22+
transitionOut="fadeOut"
23+
/>
24+
</div>
1625
);
1726

1827
Page.propTypes = {
+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import React, { useCallback, useState } from "react";
2+
import PT from "prop-types";
3+
import cn from "classnames";
4+
import _ from "lodash";
5+
import AsyncSelect from "react-select/async";
6+
import { getMemberSuggestions } from "services/teams";
7+
// import { getOptionByValue } from "utils/misc";
8+
import styles from "./styles.module.scss";
9+
10+
const selectComponents = {
11+
DropdownIndicator: () => null,
12+
IndicatorSeparator: () => null,
13+
};
14+
15+
/**
16+
* Displays search input field.
17+
*
18+
* @param {Object} props component properties
19+
* @param {string} [props.className] class name added to root element
20+
* @param {string} props.id id for input element
21+
* @param {string} props.placeholder placeholder text
22+
* @param {string} props.name name for input element
23+
* @param {'medium'|'small'} [props.size] field size
24+
* @param {function} props.onChange function called when input value changes
25+
* @param {string} props.value input value
26+
* @returns {JSX.Element}
27+
*/
28+
const SearchAutocomplete = ({
29+
className,
30+
id,
31+
size = "medium",
32+
onChange,
33+
placeholder,
34+
value,
35+
}) => {
36+
// const option = getOptionByValue(options, value);
37+
const [savedInput, setSavedInput] = useState("");
38+
39+
const onValueChange = useCallback(
40+
(option) => {
41+
onChange(option.value);
42+
},
43+
[onChange]
44+
);
45+
46+
return (
47+
<div className={cn(styles.container, styles[size], className)}>
48+
<span className={styles.icon} />
49+
<AsyncSelect
50+
className={styles.select}
51+
classNamePrefix="custom"
52+
components={selectComponents}
53+
id={id}
54+
isSearchable={true}
55+
// menuIsOpen={true} // for debugging
56+
// onChange={onOptionChange}
57+
// onMenuOpen={onMenuOpen}
58+
// onMenuClose={onMenuClose}
59+
value={{ value, label: value }}
60+
onInputChange={setSavedInput}
61+
onFocus={() => {
62+
setSavedInput("");
63+
onChange(savedInput);
64+
}}
65+
placeholder={placeholder}
66+
onChange={onValueChange}
67+
noOptionsMessage={() => "No options"}
68+
loadingMessage={() => "Loading..."}
69+
loadOptions={loadSuggestions}
70+
blurInputOnSelect
71+
/>
72+
</div>
73+
);
74+
};
75+
76+
const loadSuggestions = (inputVal) => {
77+
return getMemberSuggestions(inputVal)
78+
.then((res) => {
79+
const users = _.get(res, "data.result.content", []);
80+
return users.map((user) => ({
81+
label: user.handle,
82+
value: user.handle,
83+
}));
84+
})
85+
.catch(() => {
86+
console.warn("could not get suggestions");
87+
return [];
88+
});
89+
};
90+
91+
SearchAutocomplete.propTypes = {
92+
className: PT.string,
93+
id: PT.string.isRequired,
94+
size: PT.oneOf(["medium", "small"]),
95+
name: PT.string.isRequired,
96+
onChange: PT.func.isRequired,
97+
options: PT.array,
98+
placeholder: PT.string,
99+
value: PT.oneOfType([PT.number, PT.string]),
100+
};
101+
102+
export default SearchAutocomplete;

0 commit comments

Comments
 (0)