Added some basic shared components, utils, hooks

This commit is contained in:
ireic
2019-12-08 03:49:49 +01:00
parent 6be3ac2e77
commit 3143f66a0f
82 changed files with 40121 additions and 5 deletions

View File

@@ -1,9 +1,18 @@
{ {
"presets": ["@babel/preset-env", "@babel/react"], "presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "entry",
"corejs": 3
}
],
"@babel/react"
],
"plugins": [ "plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }], ["@babel/plugin-proposal-decorators", { "legacy": true }],
"@babel/plugin-proposal-export-namespace-from", "@babel/plugin-proposal-export-namespace-from",
"@babel/plugin-syntax-dynamic-import", "@babel/plugin-syntax-dynamic-import",
["@babel/plugin-proposal-class-properties", { "loose": true }], ["@babel/plugin-proposal-class-properties", { "loose": true }]
] ]
} }

View File

@@ -12,7 +12,10 @@
"jest": true "jest": true
}, },
"extends": ["airbnb", "prettier", "prettier/react"], "extends": ["airbnb", "prettier", "prettier/react"],
"plugins": ["react-hooks"],
"rules": { "rules": {
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"radix": 0, "radix": 0,
"no-restricted-syntax": 0, "no-restricted-syntax": 0,
"no-await-in-loop": 0, "no-await-in-loop": 0,
@@ -25,10 +28,12 @@
"import/no-cycle": 0, "import/no-cycle": 0,
"react/no-array-index-key": 0, "react/no-array-index-key": 0,
"react/forbid-prop-types": 0, "react/forbid-prop-types": 0,
"react/state-in-constructor": 0,
"react/jsx-props-no-spreading": 0,
"jsx-a11y/click-events-have-key-events": 0 "jsx-a11y/click-events-have-key-events": 0
}, },
"settings": { "settings": {
// Allows us to use absolute imports within codebase // Allows us to lint absolute "src" imports within codebase
"import/resolver": { "import/resolver": {
"node": { "node": {
"moduleDirectory": ["node_modules", "src/"] "moduleDirectory": ["node_modules", "src/"]

8
client/jsconfig.json Normal file
View File

@@ -0,0 +1,8 @@
// This config allows VSCode intellisense to work with absolute "src" imports and jsx files
{
"compilerOptions": {
"baseUrl": "./src",
"jsx": "react"
},
"exclude": ["node_modules", "dist", "dev"]
}

View File

@@ -2432,6 +2432,11 @@
"integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=",
"dev": true "dev": true
}, },
"core-js": {
"version": "3.4.7",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.4.7.tgz",
"integrity": "sha512-qaPVGw30J1wQ0GR3GvoPqlGf9GZfKKF4kFC7kiHlcsPTqH3txrs9crCp3ZiMAXuSenhz89Jnl4GZs/67S5VOSg=="
},
"core-js-compat": { "core-js-compat": {
"version": "3.4.7", "version": "3.4.7",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.4.7.tgz", "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.4.7.tgz",
@@ -2598,6 +2603,11 @@
"integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==", "integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg==",
"dev": true "dev": true
}, },
"csstype": {
"version": "2.6.7",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.7.tgz",
"integrity": "sha512-9Mcn9sFbGBAdmimWb2gLVDtFJzeKtDGIr76TUqmjZrw9LFXBMSU70lcs+C0/7fyCd6iBDqmksUcCOUIkisPHsQ=="
},
"cyclist": { "cyclist": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz", "resolved": "https://registry.npmjs.org/cyclist/-/cyclist-1.0.1.tgz",
@@ -2841,6 +2851,15 @@
"utila": "~0.4" "utila": "~0.4"
} }
}, },
"dom-helpers": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.3.tgz",
"integrity": "sha512-nZD1OtwfWGRBWlpANxacBEZrEuLa16o1nh7YopFWeoF68Zt8GGEmzHu6Xv4F3XaFIC+YXtTLrzgqKxFgLEe4jw==",
"requires": {
"@babel/runtime": "^7.6.3",
"csstype": "^2.6.7"
}
},
"dom-serializer": { "dom-serializer": {
"version": "0.2.2", "version": "0.2.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
@@ -7661,6 +7680,17 @@
"prop-types": "^15.6.0" "prop-types": "^15.6.0"
} }
}, },
"react-transition-group": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz",
"integrity": "sha512-1qRV1ZuVSdxPlPf4O8t7inxUGpdyO5zG9IoNfJxSO0ImU2A1YWkEQvFPuIPZmMLkg5hYs7vv5mMOyfgSkvAwvw==",
"requires": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
}
},
"read-pkg": { "read-pkg": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",

View File

@@ -37,6 +37,7 @@
"dependencies": { "dependencies": {
"axios": "^0.19.0", "axios": "^0.19.0",
"color": "^3.1.2", "color": "^3.1.2",
"core-js": "^3.4.7",
"history": "^4.10.1", "history": "^4.10.1",
"jwt-decode": "^2.2.0", "jwt-decode": "^2.2.0",
"lodash": "^4.17.15", "lodash": "^4.17.15",
@@ -47,6 +48,7 @@
"react-dom": "^16.12.0", "react-dom": "^16.12.0",
"react-router-dom": "^5.1.2", "react-router-dom": "^5.1.2",
"react-textarea-autosize": "^7.1.2", "react-textarea-autosize": "^7.1.2",
"react-transition-group": "^4.3.0",
"styled-components": "^4.4.1", "styled-components": "^4.4.1",
"sweet-pubsub": "^1.1.2" "sweet-pubsub": "^1.1.2"
}, },

View File

@@ -0,0 +1,3 @@
import { createBrowserHistory } from 'history';
export default createBrowserHistory();

View File

@@ -0,0 +1,19 @@
import React from 'react';
import Toast from './Toast';
import Routes from './Routes';
import NormalizeStyles from './NormalizeStyles';
import FontStyles from './FontStyles';
import BaseStyles from './BaseStyles';
const App = () => (
<>
<NormalizeStyles />
<FontStyles />
<BaseStyles />
<Toast />
<Routes />
</>
);
export default App;

View File

@@ -0,0 +1,6 @@
import styled from 'styled-components';
export const Main = styled.main`
width: 100%;
padding-left: 75px;
`;

View File

@@ -0,0 +1,108 @@
import { createGlobalStyle } from 'styled-components';
import { color, font, mixin } from 'shared/utils/styles';
export default createGlobalStyle`
html, body, #root {
height: 100%;
min-height: 100%;
}
body {
color: ${color.textDarkest};
-webkit-tap-highlight-color: transparent;
line-height: 1.2;
${font.size(16)}
${font.regular}
}
#root {
display: flex;
flex-direction: column;
}
button,
input,
optgroup,
select,
textarea {
${font.regular}
}
*, *:after, *:before, input[type="search"] {
box-sizing: border-box;
}
a, a:hover, a:visited, a:active {
text-decoration: none;
}
ul {
list-style: none;
}
ul, li, ol, dd, h1, h2, h3, h4, h5, h6, p {
padding: 0;
margin: 0;
}
h1, h2, h3, h4, h5, h6, strong {
${font.bold}
}
button {
background: none;
border: none;
}
/* Workaround for IE11 focus highlighting for select elements */
select::-ms-value {
background: none;
color: #42413d;
}
[role="button"], button, input, select, textarea {
outline: none;
&:focus {
outline: none;
}
&:disabled {
opacity: 1;
}
}
[role="button"], button, input, textarea {
appearance: none;
}
select:-moz-focusring {
color: transparent;
text-shadow: 0 0 0 #000;
}
select::-ms-expand {
display: none;
}
select option {
color: ${color.textDarkest};
}
p {
line-height: 1.6;
a {
${mixin.link()}
}
}
textarea {
line-height: 1.6;
}
body, select {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
html {
touch-action: manipulation;
}
${mixin.placeholderColor(color.textLightBlue)}
`;

View File

@@ -0,0 +1,53 @@
import { createGlobalStyle } from 'styled-components';
import BlackWoff2 from 'shared/assets/fonts/CircularStd-Black.woff2';
import BoldWoff2 from 'shared/assets/fonts/CircularStd-Bold.woff2';
import MediumWoff2 from 'shared/assets/fonts/CircularStd-Medium.woff2';
import BookWoff2 from 'shared/assets/fonts/CircularStd-Book.woff2';
import BlackWoff from 'shared/assets/fonts/CircularStd-Black.woff';
import BoldWoff from 'shared/assets/fonts/CircularStd-Bold.woff';
import MediumWoff from 'shared/assets/fonts/CircularStd-Medium.woff';
import BookWoff from 'shared/assets/fonts/CircularStd-Book.woff';
import IconsSvg from 'shared/assets/icons/jira.svg';
import IconsTtf from 'shared/assets/icons/jira.ttf';
import IconsWoff from 'shared/assets/icons/jira.woff';
export default createGlobalStyle`
@font-face {
font-family: "CircularStdBlack";
src: url("${BlackWoff2}") format("woff2"),
url("${BlackWoff}") format("woff");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "CircularStdBold";
src: url("${BoldWoff2}") format("woff2"),
url("${BoldWoff}") format("woff");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "CircularStdMedium";
src: url("${MediumWoff2}") format("woff2"),
url("${MediumWoff}") format("woff");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "CircularStdBook";
src: url("${BookWoff2}") format("woff2"),
url("${BookWoff}") format("woff");
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: "jira";
src:
url("${IconsTtf}") format("truetype"),
url("${IconsWoff}") format("woff"),
url("${IconsSvg}#jira") format("svg");
font-weight: normal;
font-style: normal;
}
`;

View File

@@ -0,0 +1,107 @@
import styled from 'styled-components';
import { NavLink } from 'react-router-dom';
import { font, sizes, color, mixin, zIndexValues } from 'shared/utils/styles';
import Logo from 'shared/components/Logo';
export const NavLeft = styled.aside`
z-index: ${zIndexValues.navLeft};
position: absolute;
top: 0;
left: 0;
overflow-x: hidden;
height: 100%;
width: ${sizes.appNavBarLeftWidth}px;
background: ${color.primary};
transition: all 0.1s;
${mixin.hardwareAccelerate}
&:hover {
width: 260px;
box-shadow: 0 0 50px 0 rgba(0, 0, 0, 0.6);
}
`;
export const LogoLink = styled(NavLink)`
display: block;
position: relative;
left: 0;
margin: 40px 0 40px;
transition: left 0.1s;
&:before {
display: inline-block;
content: '';
position: absolute;
top: 0;
right: 0;
height: 50px;
width: 20px;
background: ${color.primary};
}
${NavLeft}:hover & {
left: 3px;
&:before {
display: none;
}
}
`;
export const StyledLogo = styled(Logo)`
display: inline-block;
margin-left: 13px;
padding: 10px;
${mixin.clickable}
`;
export const IconLink = styled(NavLink)`
display: block;
position: relative;
width: 100%;
height: 60px;
line-height: 60px;
padding-left: 67px;
color: rgba(255, 255, 255, 0.75);
transition: color 0.1s;
${mixin.clickable}
&:before {
content: '';
display: none;
position: absolute;
top: 5px;
right: 0;
height: 50px;
width: 5px;
background: #fff;
border-radius: 6px 0 0 6px;
}
&.active,
&:hover {
color: #fff;
}
&.active:before {
display: inline-block;
}
&:hover {
background: rgba(255, 255, 255, 0.1);
}
i {
position: absolute;
left: 27px;
}
`;
export const LinkText = styled.div`
position: relative;
right: 12px;
visibility: hidden;
opacity: 0;
text-transform: uppercase;
transition: all 0.1s;
transition-property: right, visibility, opacity;
${font.bold}
${font.size(12)}
${NavLeft}:hover & {
right: 0;
visibility: visible;
opacity: 1;
}
`;

View File

@@ -0,0 +1,26 @@
import React from 'react';
import { Icon } from 'shared/components';
import { NavLeft, LogoLink, StyledLogo, IconLink, LinkText } from './Styles';
const NavbarLeft = () => (
<NavLeft>
<LogoLink to="/">
<StyledLogo color="#fff" />
</LogoLink>
<IconLink to="/projects">
<Icon type="archive" size={16} />
<LinkText>Projects</LinkText>
</IconLink>
<IconLink to="/subcontractors">
<Icon type="briefcase" size={16} />
<LinkText>Subcontractors</LinkText>
</IconLink>
<IconLink to="/bids">
<Icon type="file-text" size={20} left={-2} />
<LinkText>Bids</LinkText>
</IconLink>
</NavLeft>
);
export default NavbarLeft;

View File

@@ -0,0 +1,152 @@
import { createGlobalStyle } from 'styled-components';
/** DO NOT ALTER THIS FILE. It is a copy of https://necolas.github.io/normalize.css/ */
export default createGlobalStyle`
html {
line-height: 1.15;
-webkit-text-size-adjust: 100%;
}
body {
margin: 0;
}
main {
display: block;
}
h1 {
font-size: 2em;
margin: 0.67em 0;
}
hr {
box-sizing: content-box;
height: 0;
overflow: visible;
}
pre {
font-family: monospace, monospace;
font-size: 1em;
}
a {
background-color: transparent;
}
abbr[title] {
border-bottom: none;
text-decoration: underline;
text-decoration: underline dotted;
}
b,
strong {
font-weight: bolder;
}
code,
kbd,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
small {
font-size: 80%;
}
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sub {
bottom: -0.25em;
}
sup {
top: -0.5em;
}
img {
border-style: none;
}
button,
input,
optgroup,
select,
textarea {
font-family: inherit;
font-size: 100%;
line-height: 1.15;
margin: 0;
}
button,
input {
overflow: visible;
}
button,
select {
text-transform: none;
}
button,
[type="button"],
[type="reset"],
[type="submit"] {
-webkit-appearance: button;
}
button::-moz-focus-inner,
[type="button"]::-moz-focus-inner,
[type="reset"]::-moz-focus-inner,
[type="submit"]::-moz-focus-inner {
border-style: none;
padding: 0;
}
button:-moz-focusring,
[type="button"]:-moz-focusring,
[type="reset"]:-moz-focusring,
[type="submit"]:-moz-focusring {
outline: 1px dotted ButtonText;
}
fieldset {
padding: 0.35em 0.75em 0.625em;
}
legend {
box-sizing: border-box;
color: inherit;
display: table;
max-width: 100%;
padding: 0;
white-space: normal;
}
progress {
vertical-align: baseline;
}
textarea {
overflow: auto;
}
[type="checkbox"],
[type="radio"] {
box-sizing: border-box;
padding: 0;
}
[type="number"]::-webkit-inner-spin-button,
[type="number"]::-webkit-outer-spin-button {
height: auto;
}
[type="search"] {
-webkit-appearance: textfield;
outline-offset: -2px;
}
[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
::-webkit-file-upload-button {
-webkit-appearance: button;
font: inherit;
}
details {
display: block;
}
summary {
display: list-item;
}
template {
display: none;
}
[hidden] {
display: none;
}
`;

View File

@@ -0,0 +1,21 @@
import React from 'react';
import history from 'browserHistory';
import { Router, Switch, Route } from 'react-router-dom';
import PageNotFound from 'components/PageNotFound';
import NavbarLeft from './NavbarLeft';
import { Main } from './AppStyles';
const Routes = () => (
<Router history={history}>
<Main>
<NavbarLeft />
<Switch>
<Route component={PageNotFound} />
</Switch>
</Main>
</Router>
);
export default Routes;

View File

@@ -0,0 +1,58 @@
import styled from 'styled-components';
import { color, font, mixin, zIndexValues } from 'shared/utils/styles';
export const Container = styled.div`
z-index: ${zIndexValues.modal + 1};
position: fixed;
right: 30px;
top: 50px;
`;
export const StyledToast = styled.div`
position: relative;
margin-bottom: 5px;
width: 300px;
padding: 15px 20px;
border-radius: 4px;
color: #fff;
background: ${props => color[props.type]};
cursor: pointer;
transition: all 0.15s;
${mixin.clearfix}
${mixin.hardwareAccelerate}
&.jira-toast-enter,
&.jira-toast-exit.jira-toast-exit-active {
opacity: 0;
right: -10px;
}
&.jira-toast-exit,
&.jira-toast-enter.jira-toast-enter-active {
opacity: 1;
right: 0;
}
i {
position: absolute;
top: 13px;
right: 14px;
font-size: 22px;
cursor: pointer;
color: #fff;
}
`;
export const Title = styled.div`
padding-right: 22px;
${font.size(16)}
${font.medium}
`;
export const Message = styled.div`
padding: 8px 10px 0 0;
white-space: pre-wrap;
${font.size(14)}
${font.medium}
`;

View File

@@ -0,0 +1,62 @@
import React, { Component } from 'react';
import { CSSTransition, TransitionGroup } from 'react-transition-group';
import pubsub from 'sweet-pubsub';
import { uniqueId } from 'lodash';
import { Icon } from 'shared/components';
import { Container, StyledToast, Title, Message } from './Styles';
class Toast extends Component {
state = { toasts: [] };
componentDidMount() {
pubsub.on('toast', this.addToast);
}
componentWillUnmount() {
pubsub.off('toast', this.addToast);
}
addToast = ({ type = 'success', title, message, duration = 5 }) => {
const id = uniqueId('toast-');
this.setState(state => ({
toasts: [...state.toasts, { id, type, title, message }],
}));
if (duration) {
setTimeout(() => this.removeToast(id), duration * 1000);
}
};
removeToast = id => {
this.setState(state => ({
toasts: state.toasts.filter(toast => toast.id !== id),
}));
};
render() {
const { toasts } = this.state;
return (
<Container>
<TransitionGroup>
{toasts.map(toast => (
<CSSTransition key={toast.id} classNames="jira-toast" timeout={200}>
<StyledToast
key={toast.id}
type={toast.type}
onClick={() => this.removeToast(toast.id)}
>
<Icon type="close" />
{toast.title && <Title>{toast.title}</Title>}
{toast.message && <Message>{toast.message}</Message>}
</StyledToast>
</CSSTransition>
))}
</TransitionGroup>
</Container>
);
}
}
export default Toast;

View File

@@ -0,0 +1,23 @@
import styled from 'styled-components';
import { color, font } from 'shared/utils/styles';
export const Wrapper = styled.div`
margin: 50px auto 0;
max-width: 500px;
padding: 50px 50px 60px;
text-align: center;
border-radius: 4px;
background: ${color.backgroundLight};
`;
export const Heading = styled.h1`
${font.size(60)}
`;
export const Message = styled.p`
color: ${color.textDark};
padding: 10px 0 30px;
line-height: 1.35;
${font.size(20)}
`;

View File

@@ -0,0 +1,104 @@
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import {
Button,
ConfirmModal,
Avatar,
DatePicker,
Input,
Modal,
Select,
Textarea,
Spinner,
} from 'shared/components';
import { Wrapper, Heading, Message } from './Styles';
const PageNotFound = () => {
const [dateValue, setDateValue] = useState(null);
const [inputValue, setInputValue] = useState('');
const [isModalOpen, setModalOpen] = useState(false);
const [selectValue, setSelectValue] = useState('');
const [selectOptions, setSelectOptions] = useState([
{ label: 'one', value: '1' },
{ label: 'two', value: '2' },
{ label: 'three', value: '3' },
{ label: 'four', value: '4' },
{ label: 'five', value: '5' },
{ label: 'six', value: '6' },
{ label: 'seven', value: '7' },
{ label: 'eight', value: '8' },
{ label: 'nine', value: '9' },
{ label: 'ten', value: '10' },
]);
console.log('ha');
return (
<Wrapper>
<Heading>404</Heading>
<Message>We cannot find the page you are looking for.</Message>
<div style={{ textAlign: 'left' }}>
<Avatar name="Ivor Reic" size={40} />
<ConfirmModal
renderLink={modal => <Button onClick={modal.open}>Yo</Button>}
confirmInput="YAY"
onConfirm={modal => {
console.log('CONFIRMED!');
modal.close();
}}
/>
<DatePicker placeholder="Select date" value={dateValue} onChange={setDateValue} />
<Input
placeholder="Write anything mon"
value={inputValue}
onChange={(event, value) => setInputValue(value)}
/>
<Textarea
placeholder="Write anything mon"
value={inputValue}
onChange={(event, value) => setInputValue(value)}
/>
<Button onClick={() => setModalOpen(true)}>OPEN MODAL CONTROLLED</Button>
<Modal
// renderLink={modal => <Button onClick={modal.open}>OPEN MODAL</Button>}
isOpen={isModalOpen}
onClose={() => setModalOpen(false)}
renderContent={modal => (
<>
<h1>Nice modal bro</h1>
<h1>Nice modal bro</h1>
<Button onClick={modal.close}>Close</Button>
<Modal
renderLink={innerModal => <Button onClick={innerModal.open}>Open Modal</Button>}
renderContent={innerModal => (
<>
<h1>Nice innerModal bro</h1>
<Button onClick={innerModal.close}>Close</Button>
</>
)}
/>
</>
)}
/>
<Select
isMulti
value={selectValue}
onChange={setSelectValue}
placeholder="Type to search"
onCreate={(newOptionName, selectOptionValue) => {
setTimeout(() => {
setSelectOptions([...selectOptions, { label: newOptionName, value: newOptionName }]);
selectOptionValue(newOptionName);
}, 1000);
}}
options={selectOptions}
/>
<Spinner />
</div>
<Link to="/">
<Button>Home</Button>
</Link>
</Wrapper>
);
};
export default PageNotFound;

7
client/src/index.jsx Normal file → Executable file
View File

@@ -1,5 +1,8 @@
import React from 'react'; import 'core-js/stable';
import React from 'react';
import ReactDOM from 'react-dom'; import ReactDOM from 'react-dom';
ReactDOM.render(<h1>YOOOOOOOOOO</h1>, document.getElementById('root')); import App from 'components/App/App';
ReactDOM.render(<App />, document.getElementById('root'));

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 433 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 341 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 432 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="jira" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="960" descent="-64" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" horiz-adv-x="0" d="" />
<glyph unicode="&#xe900;" glyph-name="arrow-left" d="M542.165 179.499l-225.835 225.835h494.336c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667h-494.336l225.835 225.835c16.683 16.683 16.683 43.691 0 60.331s-43.691 16.683-60.331 0l-298.667-298.667c-4.096-4.096-7.168-8.789-9.259-13.824-4.309-10.453-4.309-22.272 0-32.725 2.048-4.949 5.077-9.557 9.045-13.611 0.085-0.085 0.128-0.128 0.213-0.213l298.667-298.667c16.683-16.683 43.691-16.683 60.331 0s16.683 43.691 0 60.331z" />
<glyph unicode="&#xe901;" glyph-name="bell" d="M725.333 618.667c0-80.811 9.003-147.541 22.741-202.283 12.288-48.981 28.331-88.235 44.885-119.211 3.755-7.040 7.552-13.653 11.349-19.84h-584.619c3.797 6.187 7.552 12.8 11.349 19.84 16.555 30.976 32.597 70.187 44.885 119.211 13.739 54.741 22.741 121.472 22.741 202.283 0 28.971 5.76 56.491 16.128 81.579 10.795 26.069 26.667 49.579 46.336 69.291s43.221 35.541 69.291 46.336c25.088 10.368 52.608 16.128 81.579 16.128s56.491-5.76 81.579-16.128c26.069-10.795 49.579-26.624 69.291-46.336s35.541-43.221 46.336-69.291c10.368-25.088 16.128-52.608 16.128-81.579zM810.667 618.667c0 40.363-8.021 78.976-22.613 114.219-15.147 36.565-37.333 69.461-64.853 96.981s-60.373 49.707-96.981 64.853c-35.243 14.592-73.856 22.613-114.219 22.613s-78.976-8.021-114.219-22.613c-36.608-15.189-69.461-37.376-96.981-64.853s-49.664-60.373-64.853-96.981c-14.592-35.243-22.613-73.856-22.613-114.219 0-74.453-8.277-134.187-20.181-181.547-10.624-42.368-24.149-75.008-37.333-99.669-13.397-25.003-26.539-41.984-36.224-52.651-8.96-9.899-15.061-14.464-15.744-14.933-19.2-13.184-24.32-39.381-11.349-58.837 8.192-12.288 21.589-18.944 35.499-19.029h768c23.552 0 42.667 19.115 42.667 42.667 0 14.763-7.467 27.733-18.987 35.499 0.384-0.256-5.845 4.224-15.275 14.635-9.685 10.667-22.827 27.648-36.224 52.651-13.184 24.661-26.709 57.301-37.333 99.669-11.904 47.36-20.181 107.093-20.181 181.547zM548.907 85.419c-2.944-5.077-6.699-9.216-10.965-12.501-4.48-3.413-9.557-5.888-14.976-7.339s-11.093-1.835-16.64-1.067c-5.333 0.725-10.667 2.475-15.701 5.419-6.912 4.011-12.075 9.472-15.317 15.232-11.691 20.48-37.717 27.605-58.197 15.915s-27.605-37.717-15.915-58.197c10.667-18.731 26.581-35.115 46.635-46.763 14.933-8.661 30.976-13.995 47.232-16.171 16.853-2.261 33.792-1.109 49.877 3.2s31.36 11.691 44.885 22.016c13.013 9.941 24.32 22.571 32.981 37.461 11.819 20.395 4.864 46.507-15.488 58.325s-46.507 4.864-58.325-15.488z" />
<glyph unicode="&#xe902;" glyph-name="settings" d="M825.6 672l96 96-89.6 89.6-96-96c-44.8 32-102.4 57.6-160 64v134.4h-128v-134.4c-57.6-12.8-115.2-32-160-64l-89.6 96-96-96 96-96c-32-38.4-57.6-96-64-153.6h-134.4v-128h134.4c12.8-57.6 32-115.2 64-160l-96-96 89.6-89.6 96 96c44.8-32 102.4-57.6 160-64v-134.4h128v134.4c57.6 12.8 115.2 32 160 64l96-96 89.6 89.6-96 96c32 44.8 57.6 102.4 64 160h134.4v128h-134.4c-6.4 57.6-32 115.2-64 160zM512 256c-108.8 0-192 83.2-192 192s83.2 192 192 192c108.8 0 192-83.2 192-192s-83.2-192-192-192z" />
<glyph unicode="&#xe903;" glyph-name="calendar" d="M298.667 874.667v-42.667h-85.333c-17.28 0-33.835-3.456-48.981-9.728-15.659-6.485-29.739-16-41.515-27.776s-21.291-25.856-27.776-41.515c-6.272-15.147-9.728-31.701-9.728-48.981v-597.333c0-17.28 3.456-33.835 9.728-48.981 6.485-15.701 16-29.781 27.776-41.557s25.856-21.291 41.557-27.776c15.104-6.229 31.659-9.685 48.939-9.685h597.333c17.28 0 33.835 3.456 48.981 9.728 15.701 6.485 29.781 16 41.557 27.776s21.291 25.856 27.776 41.557c6.229 15.104 9.685 31.659 9.685 48.939v597.333c0 17.28-3.456 33.835-9.728 48.981-6.485 15.701-16 29.781-27.776 41.557s-25.856 21.291-41.557 27.776c-15.104 6.229-31.659 9.685-48.939 9.685h-85.333v42.667c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667v-42.667h-256v42.667c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667zM853.333 576h-682.667v128c0 5.845 1.152 11.349 3.2 16.299 2.133 5.205 5.333 9.899 9.301 13.867s8.661 7.125 13.867 9.301c4.949 2.048 10.453 3.2 16.299 3.2h85.333v-42.667c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667v42.667h256v-42.667c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667v42.667h85.333c5.845 0 11.349-1.152 16.299-3.2 5.205-2.133 9.899-5.333 13.867-9.301s7.125-8.661 9.301-13.867c2.048-4.949 3.2-10.453 3.2-16.299zM170.667 490.667h682.667v-384c0-5.845-1.152-11.349-3.2-16.299-2.133-5.205-5.333-9.899-9.301-13.867s-8.661-7.125-13.867-9.301c-4.949-2.048-10.453-3.2-16.299-3.2h-597.333c-5.845 0-11.349 1.152-16.299 3.2-5.205 2.133-9.899 5.333-13.867 9.301s-7.125 8.661-9.301 13.867c-2.048 4.949-3.2 10.453-3.2 16.299z" />
<glyph unicode="&#xe904;" glyph-name="check" d="M823.168 734.165l-439.168-439.168-183.168 183.168c-16.683 16.683-43.691 16.683-60.331 0s-16.683-43.691 0-60.331l213.333-213.333c16.683-16.683 43.691-16.683 60.331 0l469.333 469.333c16.683 16.683 16.683 43.691 0 60.331s-43.691 16.683-60.331 0z" />
<glyph unicode="&#xe905;" glyph-name="chevron-down" d="M225.835 545.835l256-256c16.683-16.683 43.691-16.683 60.331 0l256 256c16.683 16.683 16.683 43.691 0 60.331s-43.691 16.683-60.331 0l-225.835-225.835-225.835 225.835c-16.683 16.683-43.691 16.683-60.331 0s-16.683-43.691 0-60.331z" />
<glyph unicode="&#xe906;" glyph-name="chevron-left" d="M670.165 222.165l-225.835 225.835 225.835 225.835c16.683 16.683 16.683 43.691 0 60.331s-43.691 16.683-60.331 0l-256-256c-16.683-16.683-16.683-43.691 0-60.331l256-256c16.683-16.683 43.691-16.683 60.331 0s16.683 43.691 0 60.331z" />
<glyph unicode="&#xe907;" glyph-name="chevron-right" d="M414.165 161.835l256 256c16.683 16.683 16.683 43.691 0 60.331l-256 256c-16.683 16.683-43.691 16.683-60.331 0s-16.683-43.691 0-60.331l225.835-225.835-225.835-225.835c-16.683-16.683-16.683-43.691 0-60.331s43.691-16.683 60.331 0z" />
<glyph unicode="&#xe908;" glyph-name="chevron-up" d="M798.165 350.165l-256 256c-16.683 16.683-43.691 16.683-60.331 0l-256-256c-16.683-16.683-16.683-43.691 0-60.331s43.691-16.683 60.331 0l225.835 225.835 225.835-225.835c16.683-16.683 43.691-16.683 60.331 0s16.683 43.691 0 60.331z" />
<glyph unicode="&#xe909;" glyph-name="clock" d="M981.333 448c0 63.488-12.629 124.16-35.541 179.499-23.808 57.472-58.667 109.141-101.888 152.363s-94.891 78.123-152.363 101.888c-55.381 22.955-116.053 35.584-179.541 35.584s-124.16-12.629-179.499-35.541c-57.472-23.808-109.141-58.667-152.363-101.931s-78.123-94.891-101.931-152.363c-22.912-55.339-35.541-116.011-35.541-179.499s12.629-124.16 35.541-179.499c23.808-57.472 58.667-109.141 101.888-152.363s94.891-78.123 152.363-101.888c55.381-22.955 116.053-35.584 179.541-35.584s124.16 12.629 179.499 35.541c57.472 23.808 109.141 58.667 152.363 101.888s78.123 94.891 101.888 152.363c22.955 55.381 35.584 116.053 35.584 179.541zM896 448c0-52.096-10.368-101.675-29.056-146.859-19.456-46.976-47.957-89.259-83.413-124.672s-77.739-63.957-124.672-83.413c-45.184-18.688-94.763-29.056-146.859-29.056s-101.675 10.368-146.859 29.056c-46.976 19.456-89.259 47.957-124.672 83.413-35.456 35.456-63.957 77.739-83.413 124.672-18.688 45.184-29.056 94.763-29.056 146.859s10.368 101.675 29.056 146.859c19.456 46.976 47.957 89.259 83.413 124.672s77.739 63.957 124.672 83.413c45.184 18.688 94.763 29.056 146.859 29.056s101.675-10.368 146.859-29.056c46.976-19.456 89.259-47.957 124.672-83.413 35.456-35.456 63.957-77.739 83.413-124.672 18.688-45.184 29.056-94.763 29.056-146.859zM469.333 704v-256c0-16.597 9.472-31.019 23.595-38.144l170.667-85.333c21.077-10.539 46.72-2.005 57.259 19.072s2.005 46.72-19.072 57.259l-147.115 73.515v229.632c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667z" />
<glyph unicode="&#xe90a;" glyph-name="download" d="M853.333 320v-170.667c0-5.845-1.152-11.349-3.2-16.299-2.133-5.205-5.333-9.899-9.301-13.867s-8.661-7.125-13.867-9.301c-4.949-2.048-10.453-3.2-16.299-3.2h-597.333c-5.845 0-11.349 1.152-16.299 3.2-5.205 2.133-9.899 5.333-13.867 9.301s-7.125 8.661-9.301 13.867c-2.048 4.949-3.2 10.453-3.2 16.299v170.667c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667v-170.667c0-17.28 3.456-33.835 9.728-48.981 6.485-15.701 16-29.781 27.776-41.557s25.856-21.291 41.557-27.776c15.104-6.229 31.659-9.685 48.939-9.685h597.333c17.28 0 33.835 3.456 48.981 9.728 15.701 6.485 29.781 16 41.557 27.776s21.291 25.856 27.776 41.557c6.229 15.104 9.685 31.659 9.685 48.939v170.667c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667zM554.667 422.997v409.003c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667v-409.003l-140.501 140.501c-16.683 16.683-43.691 16.683-60.331 0s-16.683-43.691 0-60.331l213.333-213.333c0.085-0.085 0.171-0.171 0.256-0.256 4.053-3.968 8.661-6.955 13.568-9.003 5.12-2.133 10.624-3.2 16.085-3.243 0.171 0 0.341 0 0.469 0 5.461 0.043 10.965 1.109 16.085 3.243 5.035 2.091 9.728 5.163 13.824 9.259l213.333 213.333c16.683 16.683 16.683 43.691 0 60.331s-43.691 16.683-60.331 0z" />
<glyph unicode="&#xe90b;" glyph-name="file" d="M571.008 914.091c-5.205 2.176-10.795 3.243-16.341 3.243h-298.667c-17.28 0-33.835-3.456-48.981-9.728-15.659-6.485-29.739-16-41.515-27.776s-21.291-25.856-27.776-41.515c-6.272-15.147-9.728-31.701-9.728-48.981v-682.667c0-17.28 3.456-33.835 9.728-48.981 6.485-15.701 16-29.781 27.776-41.557s25.856-21.291 41.557-27.776c15.104-6.229 31.659-9.685 48.939-9.685h512c17.28 0 33.835 3.456 48.981 9.728 15.701 6.485 29.781 16 41.557 27.776s21.291 25.856 27.776 41.557c6.229 15.104 9.685 31.659 9.685 48.939v469.333c0 11.776-4.779 22.443-12.501 30.165l-298.667 298.667c-0.043 0.043-0.128 0.085-0.171 0.171-4.053 4.011-8.704 7.040-13.653 9.088zM750.336 618.667h-153.003v153.003zM512 832v-256c0-23.552 19.115-42.667 42.667-42.667h256v-426.667c0-5.845-1.152-11.349-3.2-16.299-2.133-5.205-5.333-9.899-9.301-13.867s-8.661-7.125-13.867-9.301c-4.949-2.048-10.453-3.2-16.299-3.2h-512c-5.845 0-11.349 1.152-16.299 3.2-5.205 2.133-9.899 5.333-13.867 9.301s-7.125 8.661-9.301 13.867c-2.048 4.949-3.2 10.453-3.2 16.299v682.667c0 5.845 1.152 11.349 3.2 16.299 2.133 5.205 5.333 9.899 9.301 13.867s8.661 7.125 13.867 9.301c4.949 2.048 10.453 3.2 16.299 3.2z" />
<glyph unicode="&#xe90c;" glyph-name="plus" d="M213.333 405.333h256v-256c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667v256h256c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667h-256v256c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667v-256h-256c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667z" />
<glyph unicode="&#xe90d;" glyph-name="refresh" d="M189.995 561.749c15.445 43.648 38.827 82.133 67.883 114.432 30.208 33.579 66.645 60.587 106.88 79.744s84.096 30.549 129.237 32.939c43.392 2.304 88.021-3.755 131.669-19.2 50.603-17.92 94.123-46.421 127.275-80.213l120.704-113.451h-148.309c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667h256c0.171 0 0.299 0 0.469 0 5.845 0.043 11.435 1.323 16.469 3.499 5.205 2.261 9.856 5.504 13.739 9.515 0.555 0.597 1.152 1.195 1.664 1.835 3.072 3.541 5.547 7.637 7.296 12.032 1.749 4.352 2.773 9.045 2.944 13.952 0.085 0.64 0.085 1.237 0.085 1.835v256c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667v-157.397l-124.843 117.291c-42.325 43.093-96.896 78.635-159.701 100.864-54.4 19.243-110.208 26.837-164.608 23.979-56.491-2.987-111.317-17.195-161.493-41.131s-95.701-57.643-133.547-99.669c-36.437-40.491-65.664-88.619-84.907-143.061-7.851-22.229 3.755-46.592 25.984-54.443s46.592 3.755 54.443 25.984zM85.333 264.021l126.080-118.485c39.851-39.893 86.955-70.784 137.259-91.648 52.224-21.632 107.861-32.469 163.456-32.469s111.232 10.795 163.456 32.384c50.347 20.821 97.451 51.669 138.283 92.501 47.104 47.104 81.109 102.699 100.736 159.787 7.68 22.272-4.181 46.549-26.496 54.229s-46.549-4.181-54.229-26.496c-15.403-44.8-42.368-89.216-80.341-127.189-32.768-32.725-70.4-57.387-110.592-73.984-41.728-17.28-86.272-25.941-130.816-25.899s-89.045 8.704-130.773 25.984c-40.149 16.64-77.781 41.301-111.488 74.965l-119.467 112.299h148.267c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667h-256c-0.171 0-0.299 0-0.469 0-5.845-0.043-11.435-1.323-16.469-3.499-5.205-2.261-9.899-5.547-13.781-9.6-0.555-0.555-1.067-1.152-1.579-1.749-3.072-3.584-5.589-7.68-7.339-12.117-1.707-4.352-2.731-9.003-2.944-13.909-0.085-0.597-0.085-1.195-0.085-1.792v-256c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667z" />
<glyph unicode="&#xe90e;" glyph-name="search" d="M684.416 283.477c-1.451-1.109-2.859-2.347-4.224-3.712s-2.56-2.731-3.712-4.224c-26.752-25.771-58.24-46.549-93.013-60.971-35.072-14.507-73.6-22.571-114.133-22.571s-79.061 8.064-114.219 22.613c-36.523 15.104-69.419 37.291-96.981 64.896s-49.749 60.459-64.896 96.981c-14.507 35.115-22.571 73.643-22.571 114.176s8.064 79.061 22.613 114.219c15.104 36.48 37.291 69.419 64.853 96.981s60.501 49.749 96.981 64.853c35.157 14.549 73.685 22.613 114.219 22.613s79.061-8.064 114.219-22.613c36.523-15.104 69.419-37.291 96.981-64.896s49.749-60.459 64.896-96.981c14.507-35.115 22.571-73.643 22.571-114.176s-8.064-79.061-22.613-114.219c-14.421-34.773-35.2-66.261-60.971-93.013zM926.165 94.165l-156.8 156.8c22.4 27.989 40.96 59.179 54.869 92.843 18.773 45.312 29.099 94.933 29.099 146.859s-10.325 101.547-29.099 146.859c-19.456 47.019-48 89.301-83.371 124.672s-77.653 63.915-124.672 83.371c-45.312 18.773-94.933 29.099-146.859 29.099s-101.547-10.325-146.859-29.099c-47.019-19.456-89.301-48-124.672-83.371s-63.915-77.653-83.371-124.672c-18.773-45.312-29.099-94.933-29.099-146.859s10.325-101.547 29.099-146.859c19.456-47.019 48-89.301 83.371-124.672s77.653-63.915 124.672-83.371c45.312-18.773 94.933-29.099 146.859-29.099s101.547 10.325 146.859 29.099c33.621 13.952 64.853 32.512 92.843 54.869l156.8-156.8c16.683-16.683 43.691-16.683 60.331 0s16.683 43.691 0 60.331z" />
<glyph unicode="&#xe90f;" glyph-name="upload" d="M853.333 320v-170.667c0-5.845-1.152-11.349-3.2-16.299-2.133-5.205-5.333-9.899-9.301-13.867s-8.661-7.125-13.867-9.301c-4.949-2.048-10.453-3.2-16.299-3.2h-597.333c-5.845 0-11.349 1.152-16.299 3.2-5.205 2.133-9.899 5.333-13.867 9.301s-7.125 8.661-9.301 13.867c-2.048 4.949-3.2 10.453-3.2 16.299v170.667c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667v-170.667c0-17.28 3.456-33.835 9.728-48.981 6.485-15.701 16-29.781 27.776-41.557s25.856-21.291 41.557-27.776c15.104-6.229 31.659-9.685 48.939-9.685h597.333c17.28 0 33.835 3.456 48.981 9.728 15.701 6.485 29.781 16 41.557 27.776s21.291 25.856 27.776 41.557c6.229 15.104 9.685 31.659 9.685 48.939v170.667c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667zM469.333 729.003v-409.003c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667v409.003l140.501-140.501c16.683-16.683 43.691-16.683 60.331 0s16.683 43.691 0 60.331l-213.333 213.333c-0.043 0.043-0.128 0.085-0.171 0.171-4.053 4.011-8.704 7.040-13.653 9.088-10.453 4.309-22.229 4.309-32.683 0-4.949-2.048-9.6-5.077-13.653-9.088-0.043-0.043-0.128-0.085-0.171-0.171l-213.333-213.333c-16.683-16.683-16.683-43.691 0-60.331s43.691-16.683 60.331 0z" />
<glyph unicode="&#xe910;" glyph-name="close" d="M225.835 673.835l225.835-225.835-225.835-225.835c-16.683-16.683-16.683-43.691 0-60.331s43.691-16.683 60.331 0l225.835 225.835 225.835-225.835c16.683-16.683 43.691-16.683 60.331 0s16.683 43.691 0 60.331l-225.835 225.835 225.835 225.835c16.683 16.683 16.683 43.691 0 60.331s-43.691 16.683-60.331 0l-225.835-225.835-225.835 225.835c-16.683 16.683-43.691 16.683-60.331 0s-16.683-43.691 0-60.331z" />
<glyph unicode="&#xe911;" glyph-name="alert" d="M475.648 773.376c3.115 5.248 7.893 10.325 14.251 14.165 4.992 3.029 10.283 4.907 15.616 5.717 5.547 0.853 11.221 0.597 16.683-0.725s10.581-3.712 15.147-7.040c4.352-3.2 8.149-7.253 11.093-12.075l361.216-603.008c3.285-5.589 5.461-12.715 5.547-20.565 0.085-5.845-1.024-11.349-3.029-16.341-2.091-5.205-5.205-9.941-9.131-13.952s-8.619-7.211-13.781-9.429c-4.949-2.091-10.411-3.328-15.787-3.371l-722.731-0.085c-6.485 0.043-13.696 1.749-20.523 5.717-5.077 2.944-9.259 6.656-12.501 10.923-3.413 4.437-5.931 9.557-7.381 14.976s-1.835 11.093-1.109 16.64c0.683 5.333 2.432 10.667 5.035 15.147zM402.432 817.237l-361.387-603.307c-8.96-15.531-14.293-31.616-16.427-47.829-2.219-16.853-1.024-33.792 3.285-49.877s11.733-31.36 22.101-44.843c9.984-13.013 22.613-24.277 37.547-32.896 19.797-11.435 41.643-17.067 62.933-17.152h722.901c17.707 0.213 34.261 3.797 49.323 10.24 15.616 6.656 29.611 16.341 41.259 28.245s20.992 26.069 27.307 41.856c6.101 15.189 9.344 31.787 9.173 49.067-0.256 22.869-6.528 44.544-17.323 62.891l-361.557 603.605c-9.088 14.976-20.608 27.349-33.835 37.035-13.696 10.069-29.141 17.152-45.312 21.12s-33.152 4.779-49.92 2.219c-16.213-2.475-32.128-8.149-46.891-17.109-18.304-11.093-33.067-26.24-43.179-43.264zM469.333 576v-170.667c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667v170.667c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667zM554.667 234.667c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667 19.115-42.667 42.667-42.667 42.667 19.115 42.667 42.667z" />
<glyph unicode="&#xe912;" glyph-name="arrow-right" d="M481.835 716.501l225.835-225.835h-494.336c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667h494.336l-225.835-225.835c-16.683-16.683-16.683-43.691 0-60.331s43.691-16.683 60.331 0l298.667 298.667c4.096 4.096 7.168 8.832 9.259 13.867 2.091 5.12 3.2 10.539 3.243 15.957 0.043 5.675-1.024 11.349-3.243 16.64-2.091 5.035-5.163 9.771-9.259 13.867l-298.667 298.667c-16.683 16.683-43.691 16.683-60.331 0s-16.683-43.691 0-60.331z" />
<glyph unicode="&#xe913;" glyph-name="lock" d="M213.333 448h597.333c5.845 0 11.349-1.152 16.299-3.2 5.205-2.133 9.899-5.333 13.867-9.301s7.125-8.661 9.301-13.867c2.048-4.949 3.2-10.453 3.2-16.299v-298.667c0-5.845-1.152-11.349-3.2-16.299-2.133-5.205-5.333-9.899-9.301-13.867s-8.661-7.125-13.867-9.301c-4.949-2.048-10.453-3.2-16.299-3.2h-597.333c-5.845 0-11.349 1.152-16.299 3.2-5.205 2.133-9.899 5.333-13.867 9.301s-7.125 8.661-9.301 13.867c-2.048 4.949-3.2 10.453-3.2 16.299v298.667c0 5.845 1.152 11.349 3.2 16.299 2.133 5.205 5.333 9.899 9.301 13.867s8.661 7.125 13.867 9.301c4.949 2.048 10.453 3.2 16.299 3.2zM768 533.333v128c0 34.603-6.869 67.712-19.413 97.92-12.971 31.36-32 59.52-55.595 83.115s-51.755 42.581-83.115 55.595c-30.165 12.501-63.275 19.371-97.877 19.371s-67.712-6.869-97.92-19.413c-31.36-12.971-59.52-32-83.115-55.552s-42.581-51.755-55.552-83.115c-12.544-30.208-19.413-63.317-19.413-97.92v-128h-42.667c-17.28 0-33.835-3.456-48.981-9.728-15.701-6.485-29.781-16-41.557-27.776s-21.291-25.856-27.776-41.557c-6.229-15.104-9.685-31.659-9.685-48.939v-298.667c0-17.28 3.456-33.835 9.728-48.981 6.485-15.701 16-29.781 27.776-41.557s25.856-21.291 41.557-27.776c15.104-6.229 31.659-9.685 48.939-9.685h597.333c17.28 0 33.835 3.456 48.981 9.728 15.701 6.485 29.781 16 41.557 27.776s21.291 25.856 27.776 41.557c6.229 15.104 9.685 31.659 9.685 48.939v298.667c0 17.28-3.456 33.835-9.728 48.981-6.485 15.701-16 29.781-27.776 41.557s-25.856 21.291-41.557 27.776c-15.104 6.229-31.659 9.685-48.939 9.685zM341.333 533.333v128c0 23.211 4.608 45.227 12.928 65.237 8.619 20.864 21.333 39.637 37.077 55.424s34.56 28.459 55.424 37.077c20.011 8.32 42.027 12.928 65.237 12.928s45.227-4.608 65.237-12.928c20.864-8.619 39.637-21.333 55.424-37.077s28.459-34.56 37.077-55.424c8.32-20.011 12.928-42.027 12.928-65.237v-128z" />
<glyph unicode="&#xe914;" glyph-name="email" d="M128 622.037l359.552-251.691c14.507-10.027 33.92-10.496 48.939 0l359.509 251.691v-430.037c0-5.717-1.152-11.136-3.2-16.085-2.176-5.205-5.376-9.984-9.387-13.995s-8.789-7.211-13.995-9.387c-4.949-2.048-10.368-3.2-16.085-3.2h-682.667c-5.717 0-11.136 1.152-16.085 3.2-5.205 2.176-9.984 5.376-13.995 9.387s-7.211 8.789-9.387 13.995c-2.048 4.949-3.2 10.368-3.2 16.085zM42.667 703.488v-511.488c0-17.195 3.456-33.707 9.685-48.768 6.528-15.744 16.085-29.867 27.861-41.685s25.941-21.376 41.685-27.861c15.061-6.229 31.573-9.685 48.768-9.685h682.667c17.195 0 33.707 3.456 48.768 9.685 15.744 6.528 29.867 16.085 41.685 27.861s21.376 25.941 27.861 41.685c6.229 15.061 9.685 31.573 9.685 48.768v512c0 0.256 0 0.512 0 0.725-0.085 16.939-3.541 33.152-9.685 48-6.528 15.744-16.085 29.867-27.861 41.685s-25.941 21.376-41.685 27.861c-15.061 6.272-31.573 9.728-48.768 9.728h-682.667c-17.195 0-33.707-3.456-48.768-9.685-15.744-6.528-29.867-16.085-41.685-27.861s-21.333-25.941-27.861-41.685c-6.144-14.848-9.6-31.104-9.685-48.043 0-0.213 0-0.469 0-0.725zM891.477 723.029l-379.477-265.6-379.477 265.6c2.048 4.053 4.779 7.808 8.021 11.051 4.053 4.053 8.789 7.253 14.037 9.387 4.949 2.048 10.368 3.2 16.085 3.2h682.667c5.717 0 11.136-1.152 16.085-3.2 5.205-2.176 9.984-5.376 13.995-9.387 3.243-3.243 5.973-6.997 8.021-11.051z" />
<glyph unicode="&#xe915;" glyph-name="archive" d="M192 832h640c38.4 0 64 25.6 64 64s-25.6 64-64 64h-640c-38.4 0-64-25.6-64-64s25.6-64 64-64zM960 704h-896c-38.4 0-64-25.6-64-64v-640c0-38.4 25.6-64 64-64h896c38.4 0 64 25.6 64 64v640c0 38.4-25.6 64-64 64zM768 256h-512v256h128v-128h256v128h128v-256z" />
<glyph unicode="&#xe916;" glyph-name="briefcase" d="M1024 320v320c0 38.4-25.6 64-64 64h-192v192c0 38.4-25.6 64-64 64h-384c-38.4 0-64-25.6-64-64v-192h-192c-38.4 0-64-25.6-64-64v-320c0-38.4 25.6-64 64-64h896c38.4 0 64 25.6 64 64zM384 832h256v-128h-256v128zM64 128v-128c0-38.4 25.6-64 64-64h768c38.4 0 64 25.6 64 64v128h-896z" />
<glyph unicode="&#xe917;" glyph-name="dashboard" d="M512 960c-282.304 0-512-229.696-512-512s229.696-512 512-512c282.304 0 512 229.696 512 512s-229.696 512-512 512zM890.24 384h-334.912l-111.872 279.808-118.912-47.616 92.928-232.192h-283.712c-3.52 20.864-5.76 42.176-5.76 64 0 211.776 172.224 384 384 384s384-172.224 384-384c0-21.824-2.24-43.136-5.76-64z" />
<glyph unicode="&#xe918;" glyph-name="edit" d="M469.333 832h-298.667c-17.28 0-33.835-3.456-48.981-9.728-15.659-6.485-29.739-16-41.515-27.776s-21.291-25.856-27.776-41.515c-6.272-15.147-9.728-31.701-9.728-48.981v-597.333c0-17.28 3.456-33.835 9.728-48.981 6.485-15.701 16-29.781 27.776-41.557s25.856-21.291 41.557-27.776c15.104-6.229 31.659-9.685 48.939-9.685h597.333c17.28 0 33.835 3.456 48.981 9.728 15.701 6.485 29.781 16 41.557 27.776s21.291 25.856 27.776 41.557c6.229 15.104 9.685 31.659 9.685 48.939v298.667c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667v-298.667c0-5.845-1.152-11.349-3.2-16.299-2.133-5.205-5.333-9.899-9.301-13.867s-8.661-7.125-13.867-9.301c-4.949-2.048-10.453-3.2-16.299-3.2h-597.333c-5.845 0-11.349 1.152-16.299 3.2-5.205 2.133-9.899 5.333-13.867 9.301s-7.125 8.661-9.301 13.867c-2.048 4.949-3.2 10.453-3.2 16.299v597.333c0 5.845 1.152 11.349 3.2 16.299 2.133 5.205 5.333 9.899 9.301 13.867s8.661 7.125 13.867 9.301c4.949 2.048 10.453 3.2 16.299 3.2h298.667c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667zM759.168 883.499l-405.333-405.333c-5.205-5.163-9.259-11.947-11.221-19.84l-42.667-170.667c-1.664-6.4-1.792-13.568 0-20.693 5.717-22.869 28.885-36.779 51.755-31.061l170.667 42.667c7.125 1.749 14.080 5.504 19.84 11.221l405.333 405.333c12.715 12.715 22.357 27.435 28.885 43.179 6.784 16.341 10.112 33.707 10.112 50.987s-3.371 34.688-10.112 50.987c-6.528 15.744-16.171 30.464-28.885 43.179s-27.435 22.357-43.179 28.885c-16.341 6.784-33.707 10.155-51.029 10.155s-34.688-3.371-50.987-10.112c-15.744-6.528-30.464-16.171-43.179-28.885zM819.499 823.168c4.651 4.651 9.899 8.064 15.488 10.368 5.803 2.432 12.075 3.627 18.347 3.627s12.544-1.237 18.347-3.627c5.589-2.304 10.837-5.76 15.488-10.368s8.064-9.899 10.368-15.488c2.432-5.803 3.627-12.075 3.627-18.347s-1.237-12.544-3.627-18.347c-2.304-5.589-5.76-10.837-10.368-15.488l-396.971-396.971-90.197-22.571 22.571 90.197z" />
<glyph unicode="&#xe919;" glyph-name="delete" d="M768 661.333v-554.667c0-5.845-1.152-11.349-3.2-16.299-2.133-5.205-5.333-9.899-9.301-13.867s-8.661-7.125-13.867-9.301c-4.949-2.048-10.453-3.2-16.299-3.2h-426.667c-5.845 0-11.349 1.152-16.299 3.2-5.205 2.133-9.899 5.333-13.867 9.301s-7.125 8.661-9.301 13.867c-2.048 4.949-3.2 10.453-3.2 16.299v554.667zM725.333 746.667v42.667c0 17.28-3.456 33.835-9.728 48.981-6.485 15.701-16 29.781-27.776 41.557s-25.856 21.291-41.557 27.776c-15.104 6.229-31.659 9.685-48.939 9.685h-170.667c-17.28 0-33.835-3.456-48.981-9.728-15.659-6.485-29.739-16-41.515-27.776s-21.291-25.856-27.776-41.515c-6.272-15.147-9.728-31.701-9.728-48.981v-42.667h-170.667c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667h42.667v-554.667c0-17.28 3.456-33.835 9.728-48.981 6.485-15.701 16-29.781 27.776-41.557s25.856-21.291 41.557-27.776c15.104-6.229 31.659-9.685 48.939-9.685h426.667c17.28 0 33.835 3.456 48.981 9.728 15.701 6.485 29.781 16 41.557 27.776s21.291 25.856 27.776 41.557c6.229 15.104 9.685 31.659 9.685 48.939v554.667h42.667c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667zM384 746.667v42.667c0 5.845 1.152 11.349 3.2 16.299 2.133 5.205 5.333 9.899 9.301 13.867s8.661 7.125 13.867 9.301c4.949 2.048 10.453 3.2 16.299 3.2h170.667c5.845 0 11.349-1.152 16.299-3.2 5.205-2.133 9.899-5.333 13.867-9.301s7.125-8.661 9.301-13.867c2.048-4.949 3.2-10.453 3.2-16.299v-42.667zM384 490.667v-256c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667v256c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667zM554.667 490.667v-256c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667v256c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667z" />
<glyph unicode="&#xe91a;" glyph-name="bidRequest-inviteDeclined" d="M768 448v384h71.253c8.021 0.128 14.891-1.109 21.163-3.456 6.528-2.432 12.544-6.101 17.707-10.709 4.907-4.395 9.045-9.643 12.16-15.531 2.645-4.992 4.608-10.496 5.675-16.299v-292.053c-1.109-6.272-3.243-12.032-6.144-17.195-3.413-6.101-7.893-11.477-13.269-15.872-5.077-4.181-10.923-7.467-17.195-9.685-6.101-2.133-12.672-3.243-19.541-3.157zM384 277.333v-128c0-23.040 4.608-45.099 12.928-65.28 8.661-20.907 21.333-39.68 37.035-55.381s34.475-28.373 55.381-37.035c20.224-8.363 42.283-12.971 65.323-12.971 17.28 0 32.171 10.283 38.997 25.344l159.403 358.656h85.589c17.024-0.256 33.536 2.56 48.981 7.936 15.829 5.547 30.421 13.824 43.179 24.32 13.483 11.093 24.96 24.704 33.621 40.235 8.363 15.061 14.123 31.829 16.555 49.749 0.213 1.749 0.341 3.755 0.341 5.76v298.667c0 1.792-0.128 3.797-0.384 5.845-2.347 16.853-7.595 32.768-15.275 47.189-7.893 14.805-18.261 27.989-30.592 38.997-13.013 11.648-28.203 20.907-44.885 27.136-16.128 6.016-33.621 9.131-50.944 8.832h-595.2c-15.744 0.171-31.445-2.688-45.952-8.149-14.976-5.632-28.587-13.995-40.277-24.448-10.88-9.728-20.139-21.376-27.221-34.389-6.912-12.715-11.733-26.795-14.037-41.643l-58.88-384.085c-2.603-17.109-1.664-34.005 2.261-49.92 4.053-16.512 11.349-31.829 21.248-45.227s22.357-24.917 36.907-33.707c14.037-8.491 29.867-14.379 46.933-16.939 6.997-1.109 14.165-1.621 20.949-1.493zM469.333 320c0 23.552-19.115 42.667-42.667 42.667h-242.304c-1.621-0.043-3.968 0.085-6.528 0.469-5.803 0.896-11.051 2.859-15.659 5.632-4.821 2.901-9.003 6.741-12.331 11.264s-5.76 9.643-7.083 15.104c-1.28 5.205-1.621 10.795-0.725 16.555l58.88 383.915c0.768 5.077 2.432 9.771 4.736 14.037 2.389 4.395 5.504 8.277 9.131 11.563 3.925 3.499 8.448 6.272 13.397 8.107 4.693 1.792 9.856 2.773 15.872 2.688h438.613v-417.621l-153.941-346.368c-2.261 0.725-4.48 1.536-6.656 2.432-10.411 4.309-19.797 10.667-27.733 18.56-7.893 7.893-14.251 17.323-18.56 27.733-4.139 9.984-6.443 20.949-6.443 32.597z" />
<glyph unicode="&#xe91b;" glyph-name="bidRequest-inviteAccepted" d="M256 448v-384h-85.333c-5.845 0-11.349 1.152-16.299 3.2-5.205 2.133-9.899 5.333-13.867 9.301s-7.125 8.661-9.301 13.867c-2.048 4.949-3.2 10.453-3.2 16.299v298.667c0 5.845 1.152 11.349 3.2 16.299 2.133 5.205 5.333 9.899 9.301 13.867s8.661 7.125 13.867 9.301c4.949 2.048 10.453 3.2 16.299 3.2zM640 618.667v128c0 23.040-4.608 45.099-12.928 65.28-8.661 20.907-21.333 39.68-37.035 55.381s-34.517 28.416-55.424 37.077c-20.181 8.32-42.24 12.928-65.28 12.928-17.28 0-32.171-10.283-38.997-25.344l-159.403-358.656h-100.267c-17.28 0-33.835-3.456-48.981-9.728-15.701-6.485-29.781-16-41.557-27.776s-21.291-25.856-27.776-41.557c-6.229-15.104-9.685-31.659-9.685-48.939v-298.667c0-17.28 3.456-33.835 9.728-48.981 6.485-15.701 16-29.781 27.776-41.557s25.856-21.291 41.557-27.776c15.104-6.229 31.659-9.685 48.939-9.685h609.28c15.744-0.171 31.445 2.688 45.952 8.149 14.976 5.632 28.587 13.995 40.277 24.448 10.923 9.771 20.139 21.419 27.221 34.432 6.912 12.715 11.733 26.752 14.080 41.643l58.88 384.085c2.603 17.109 1.664 34.005-2.261 49.92-4.053 16.512-11.349 31.829-21.248 45.227-9.856 13.397-22.357 24.917-36.907 33.707-14.037 8.491-29.867 14.336-46.933 16.939-7.040 1.067-14.208 1.579-20.992 1.451zM554.667 576c0-23.552 19.115-42.667 42.667-42.667h242.304c1.621 0.043 3.968-0.085 6.528-0.469 5.803-0.896 11.051-2.859 15.659-5.632 4.821-2.901 9.003-6.741 12.331-11.264s5.76-9.643 7.083-15.104c1.28-5.205 1.621-10.795 0.725-16.555l-58.88-383.915c-0.768-5.077-2.432-9.771-4.736-14.037-2.389-4.395-5.504-8.277-9.131-11.563-3.925-3.499-8.448-6.272-13.397-8.107-4.736-1.792-9.899-2.731-15.915-2.688h-438.571v417.621l153.941 346.368c2.261-0.725 4.48-1.536 6.656-2.432 10.411-4.309 19.797-10.667 27.733-18.56s14.251-17.323 18.56-27.733c4.139-9.984 6.443-20.949 6.443-32.597z" />
<glyph unicode="&#xe91c;" glyph-name="bidRequest-bidSubmitted" d="M896 487.253v-39.253c-0.043-52.053-10.411-101.632-29.141-146.816-19.456-46.933-48.043-89.216-83.499-124.629s-77.781-63.915-124.757-83.328c-45.184-18.688-94.763-29.013-146.859-28.971s-101.632 10.411-146.816 29.141c-46.933 19.456-89.216 48.043-124.629 83.499s-63.915 77.781-83.328 124.757c-18.688 45.141-29.013 94.72-28.971 146.816s10.411 101.632 29.141 146.816c19.456 46.933 48.043 89.216 83.499 124.629s77.781 63.915 124.757 83.328c45.184 18.688 94.763 29.013 146.859 28.971 56.747-0.043 110.336-12.331 155.691-33.067 21.419-9.813 46.763-0.341 56.533 21.077s0.341 46.763-21.077 56.533c-56.619 25.856-122.283 40.747-191.104 40.789-63.488 0.043-124.16-12.544-179.499-35.456-57.515-23.723-109.227-58.581-152.491-101.803s-78.165-94.848-101.973-152.32c-22.955-55.339-35.627-115.968-35.669-179.456s12.544-124.16 35.456-179.499c23.765-57.472 58.624-109.184 101.803-152.448s94.848-78.165 152.32-101.973c55.339-22.955 115.968-35.627 179.456-35.669s124.16 12.544 179.499 35.456c57.472 23.765 109.184 58.624 152.448 101.803 43.264 43.221 78.165 94.848 101.973 152.32 22.997 55.339 35.669 115.968 35.712 179.499v39.253c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667zM908.501 819.499l-396.501-396.885-97.835 97.792c-16.683 16.683-43.691 16.683-60.331 0s-16.683-43.691 0-60.331l128-128c16.683-16.683 43.691-16.64 60.373 0l426.667 427.093c16.64 16.683 16.64 43.691-0.043 60.331s-43.691 16.64-60.331-0.043z" />
<glyph unicode="&#xe91d;" glyph-name="bidRequest-undecided" d="M469.333 576v-128c0-11.776 4.779-22.443 12.501-30.165l64-64c16.683-16.683 43.691-16.683 60.331 0s16.683 43.691 0 60.331l-51.499 51.499v110.336c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667zM654.080 137.557l-7.083-77.355c-0.512-5.675-2.048-10.837-4.395-15.445-2.432-4.821-5.76-9.088-9.813-12.672-3.883-3.456-8.405-6.187-13.312-8.021-4.693-1.749-9.856-2.731-15.232-2.731h-184.832c-5.803-0.043-11.093 1.024-15.915 2.901-4.992 1.963-9.6 4.907-13.525 8.576-3.797 3.541-6.912 7.765-9.216 12.501-2.219 4.565-3.669 9.557-4.181 14.976l-7.083 77.483c3.968-1.835 7.936-3.541 11.989-5.248 40.235-16.683 84.352-25.856 130.517-25.856s90.283 9.173 130.56 25.856c3.883 1.621 7.723 3.285 11.52 5.035zM349.312 645.675c19.413 16 41.259 29.227 64.811 38.955 30.080 12.459 63.104 19.371 97.877 19.371s67.797-6.912 97.877-19.371c31.275-12.971 59.477-31.957 83.115-55.595s42.667-51.84 55.595-83.115c12.501-30.123 19.413-63.147 19.413-97.92s-6.912-67.797-19.371-97.877c-12.971-31.275-31.957-59.477-55.595-83.115-4.437-4.437-9.045-8.704-13.781-12.8-1.493-1.28-3.029-2.56-4.565-3.84-19.413-16-41.259-29.227-64.811-38.955-30.080-12.501-63.104-19.413-97.877-19.413s-67.797 6.912-97.877 19.371c-31.275 12.971-59.477 31.957-83.115 55.595s-42.667 51.84-55.595 83.115c-12.501 30.123-19.413 63.147-19.413 97.92s6.912 67.797 19.371 97.877c12.971 31.275 31.957 59.477 55.595 83.115 4.437 4.437 9.045 8.704 13.781 12.8 1.493 1.28 3.029 2.56 4.565 3.84zM746.283 696.235l-13.44 147.413c-1.493 15.829-5.803 30.891-12.501 44.587-6.869 14.080-16.256 26.667-27.52 37.205-11.691 10.965-25.429 19.755-40.661 25.728-14.72 5.76-30.72 8.917-47.147 8.832h-185.771c-15.829-0.043-31.147-3.029-45.312-8.405-14.549-5.547-27.904-13.696-39.381-23.851-11.947-10.581-21.973-23.467-29.355-38.016-7.125-14.080-11.691-29.653-13.184-46.123l-13.397-146.517c-2.688-2.517-5.376-5.12-7.979-7.723-31.445-31.445-56.789-69.035-74.112-110.805-16.683-40.277-25.856-84.395-25.856-130.56s9.173-90.283 25.856-130.56c17.323-41.813 42.667-79.36 74.112-110.805 2.475-2.475 4.992-4.907 7.509-7.296l13.44-146.987c1.493-15.829 5.803-30.891 12.501-44.587 6.869-14.080 16.256-26.667 27.52-37.205 11.691-10.965 25.429-19.755 40.661-25.728 14.72-5.76 30.72-8.917 47.147-8.832h184.661c15.915-0.043 31.317 2.901 45.568 8.277 14.677 5.547 28.075 13.653 39.637 23.893 11.989 10.624 22.059 23.467 29.44 38.059 7.125 14.080 11.733 29.739 13.227 46.208l13.397 146.475c2.688 2.517 5.376 5.12 7.979 7.723 31.445 31.445 56.789 69.035 74.112 110.805 16.725 40.277 25.899 84.395 25.899 130.56s-9.173 90.283-25.856 130.56c-17.323 41.813-42.667 79.36-74.112 110.805-2.304 2.304-4.693 4.608-7.040 6.869zM369.92 758.443l7.083 77.355c0.512 5.632 2.048 10.795 4.352 15.403 2.432 4.779 5.76 9.088 9.771 12.629 3.883 3.456 8.363 6.144 13.227 8.021 4.693 1.792 9.813 2.816 15.232 2.816h185.429c5.803 0.043 11.093-1.024 15.915-2.901 4.992-1.963 9.6-4.907 13.525-8.576 3.797-3.541 6.912-7.765 9.216-12.501 2.219-4.565 3.669-9.557 4.181-14.976l7.083-77.696c-4.096 1.877-8.235 3.712-12.416 5.419-40.235 16.725-84.352 25.899-130.517 25.899s-90.283-9.173-130.56-25.856c-3.883-1.621-7.723-3.285-11.52-5.035z" />
<glyph unicode="&#xe91e;" glyph-name="bidRequest-pendingInvite" d="M750.336 661.333h-110.336v110.336zM883.328 649.003l-255.829 255.829c-0.043 0.043-0.128 0.085-0.171 0.171-4.053 4.011-8.704 7.040-13.653 9.088-5.205 2.176-10.795 3.243-16.341 3.243h-341.333c-17.28 0-33.835-3.456-48.981-9.728-15.659-6.485-29.739-16-41.515-27.776s-21.291-25.856-27.776-41.515c-6.272-15.147-9.728-31.701-9.728-48.981v-682.667c0-17.28 3.456-33.835 9.728-48.981 6.485-15.701 16-29.781 27.776-41.557s25.856-21.291 41.557-27.776c15.104-6.229 31.659-9.685 48.939-9.685h512c17.28 0 33.835 3.456 48.981 9.728 15.701 6.485 29.781 16 41.557 27.776s21.291 25.856 27.776 41.557c6.229 15.104 9.685 31.659 9.685 48.939v512c0 11.776-4.779 22.443-12.501 30.165zM554.667 832v-213.333c0-23.552 19.115-42.667 42.667-42.667h213.333v-469.333c0-5.845-1.152-11.349-3.2-16.299-2.133-5.205-5.333-9.899-9.301-13.867s-8.661-7.125-13.867-9.301c-4.949-2.048-10.453-3.2-16.299-3.2h-512c-5.845 0-11.349 1.152-16.299 3.2-5.205 2.133-9.899 5.333-13.867 9.301s-7.125 8.661-9.301 13.867c-2.048 4.949-3.2 10.453-3.2 16.299v682.667c0 5.845 1.152 11.349 3.2 16.299 2.133 5.205 5.333 9.899 9.301 13.867s8.661 7.125 13.867 9.301c4.949 2.048 10.453 3.2 16.299 3.2zM384 277.333h85.333v-85.333c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667v85.333h85.333c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667h-85.333v85.333c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667v-85.333h-85.333c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667z" />
<glyph unicode="&#xe91f;" glyph-name="upload-thin" horiz-adv-x="784" d="M205.111 448.016l175.719 175.806 175.741-175.806-25.831-25.853-131.654 131.697v-610.404h-36.533v610.426l-131.632-131.697-25.81 25.831zM487.303 197.038v36.555h239.234v689.179h-688.94v-689.179h246.68v-36.555h-283.213v762.267h762.007v-762.267h-275.767z" />
<glyph unicode="&#xe920;" glyph-name="folder-minus" d="M981.333 149.333v469.333c0 17.28-3.456 33.835-9.728 48.981-6.485 15.701-16 29.781-27.776 41.557s-25.856 21.291-41.557 27.776c-15.104 6.229-31.659 9.685-48.939 9.685h-361.173l-72.661 109.013c-7.765 11.52-20.736 18.987-35.499 18.987h-213.333c-17.28 0-33.835-3.456-48.981-9.728-15.659-6.485-29.739-16-41.515-27.776s-21.291-25.856-27.776-41.515c-6.272-15.147-9.728-31.701-9.728-48.981v-597.333c0-17.28 3.456-33.835 9.728-48.981 6.485-15.701 16-29.781 27.776-41.557s25.856-21.291 41.557-27.776c15.104-6.229 31.659-9.685 48.939-9.685h682.667c17.28 0 33.835 3.456 48.981 9.728 15.701 6.485 29.781 16 41.557 27.776s21.291 25.856 27.776 41.557c6.229 15.104 9.685 31.659 9.685 48.939zM896 149.333c0-5.845-1.152-11.349-3.2-16.299-2.133-5.205-5.333-9.899-9.301-13.867s-8.661-7.125-13.867-9.301c-4.949-2.048-10.453-3.2-16.299-3.2h-682.667c-5.845 0-11.349 1.152-16.299 3.2-5.205 2.133-9.899 5.333-13.867 9.301s-7.125 8.661-9.301 13.867c-2.048 4.949-3.2 10.453-3.2 16.299v597.333c0 5.845 1.152 11.349 3.2 16.299 2.133 5.205 5.333 9.899 9.301 13.867s8.661 7.125 13.867 9.301c4.949 2.048 10.453 3.2 16.299 3.2h190.507l72.661-109.013c8.192-12.245 21.589-18.901 35.499-18.987h384c5.845 0 11.349-1.152 16.299-3.2 5.205-2.133 9.899-5.333 13.867-9.301s7.125-8.661 9.301-13.867c2.048-4.949 3.2-10.453 3.2-16.299zM384 320h256c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667h-256c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667z" />
<glyph unicode="&#xe921;" glyph-name="folder-plus" d="M981.333 149.333v469.333c0 17.28-3.456 33.835-9.728 48.981-6.485 15.701-16 29.781-27.776 41.557s-25.856 21.291-41.557 27.776c-15.104 6.229-31.659 9.685-48.939 9.685h-361.173l-72.661 109.013c-7.765 11.52-20.736 18.987-35.499 18.987h-213.333c-17.28 0-33.835-3.456-48.981-9.728-15.659-6.485-29.739-16-41.515-27.776s-21.291-25.856-27.776-41.515c-6.272-15.147-9.728-31.701-9.728-48.981v-597.333c0-17.28 3.456-33.835 9.728-48.981 6.485-15.701 16-29.781 27.776-41.557s25.856-21.291 41.557-27.776c15.104-6.229 31.659-9.685 48.939-9.685h682.667c17.28 0 33.835 3.456 48.981 9.728 15.701 6.485 29.781 16 41.557 27.776s21.291 25.856 27.776 41.557c6.229 15.104 9.685 31.659 9.685 48.939zM896 149.333c0-5.845-1.152-11.349-3.2-16.299-2.133-5.205-5.333-9.899-9.301-13.867s-8.661-7.125-13.867-9.301c-4.949-2.048-10.453-3.2-16.299-3.2h-682.667c-5.845 0-11.349 1.152-16.299 3.2-5.205 2.133-9.899 5.333-13.867 9.301s-7.125 8.661-9.301 13.867c-2.048 4.949-3.2 10.453-3.2 16.299v597.333c0 5.845 1.152 11.349 3.2 16.299 2.133 5.205 5.333 9.899 9.301 13.867s8.661 7.125 13.867 9.301c4.949 2.048 10.453 3.2 16.299 3.2h190.507l72.661-109.013c8.192-12.245 21.589-18.901 35.499-18.987h384c5.845 0 11.349-1.152 16.299-3.2 5.205-2.133 9.899-5.333 13.867-9.301s7.125-8.661 9.301-13.867c2.048-4.949 3.2-10.453 3.2-16.299zM384 320h85.333v-85.333c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667v85.333h85.333c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667h-85.333v85.333c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667v-85.333h-85.333c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667z" />
<glyph unicode="&#xe922;" glyph-name="pocket" d="M170.667 853.334c-35.328 0-67.413-14.379-90.496-37.504s-37.504-55.168-37.504-90.496v-256c0-129.579 52.565-246.997 137.472-331.861s202.283-137.472 331.861-137.472 246.997 52.565 331.861 137.472 137.472 202.283 137.472 331.861v256c0 35.328-14.379 67.413-37.504 90.496s-55.168 37.504-90.496 37.504zM170.667 768h682.667c11.776 0 22.4-4.736 30.165-12.501s12.501-18.389 12.501-30.165v-256c0-106.069-42.923-201.984-112.469-271.531s-165.461-112.469-271.531-112.469-201.984 42.923-271.531 112.469-112.469 165.461-112.469 271.531v256c0 11.776 4.736 22.4 12.501 30.165s18.389 12.501 30.165 12.501zM311.168 481.835l170.667-170.667c16.683-16.683 43.691-16.683 60.331 0l170.667 170.667c16.683 16.683 16.683 43.691 0 60.331s-43.691 16.683-60.331 0l-140.501-140.501-140.501 140.501c-16.683 16.683-43.691 16.683-60.331 0s-16.683-43.691 0-60.331z" />
<glyph unicode="&#xe923;" glyph-name="eye" d="M4.523 445.739c-5.803-11.691-6.229-25.728 0-38.144 0 0 16.896-33.664 47.787-78.635 19.243-27.989 44.288-61.099 74.965-94.635 38.144-41.771 85.504-84.779 141.611-119.467 68.053-42.069 149.589-72.192 243.115-72.192s175.061 30.123 243.115 72.192c56.107 34.688 103.467 77.696 141.611 119.467 30.635 33.536 55.723 66.645 74.965 94.635 30.891 44.971 47.787 78.635 47.787 78.635 5.803 11.691 6.229 25.728 0 38.144 0 0-16.896 33.664-47.787 78.635-19.243 27.989-44.288 61.099-74.965 94.635-38.144 41.771-85.504 84.779-141.611 119.467-68.053 42.069-149.589 72.192-243.115 72.192s-175.061-30.123-243.115-72.192c-56.107-34.688-103.467-77.696-141.611-119.467-30.677-33.536-55.723-66.603-74.965-94.635-30.891-44.971-47.787-78.635-47.787-78.635zM91.307 426.667c6.955 11.989 17.365 29.056 31.317 49.408 17.493 25.429 40.107 55.296 67.627 85.376 34.347 37.589 75.733 74.923 123.477 104.448 57.6 35.584 123.776 59.435 198.272 59.435s140.672-23.851 198.229-59.435c47.744-29.525 89.131-66.859 123.477-104.448 27.477-30.080 50.133-59.947 67.627-85.376 13.995-20.352 24.405-37.376 31.317-49.408-6.955-11.989-17.365-29.056-31.317-49.408-17.493-25.429-40.107-55.296-67.627-85.376-34.347-37.589-75.733-74.923-123.477-104.448-57.557-35.584-123.733-59.435-198.229-59.435s-140.672 23.851-198.229 59.435c-47.744 29.525-89.131 66.859-123.477 104.448-27.477 30.080-50.133 59.947-67.627 85.376-13.995 20.352-24.405 37.419-31.36 49.408zM682.667 426.667c0 47.104-19.157 89.856-50.005 120.661s-73.557 50.005-120.661 50.005-89.856-19.157-120.661-50.005-50.005-73.557-50.005-120.661 19.157-89.856 50.005-120.661 73.557-50.005 120.661-50.005 89.856 19.157 120.661 50.005 50.005 73.557 50.005 120.661zM597.333 426.667c0-23.595-9.515-44.843-25.003-60.331s-36.736-25.003-60.331-25.003-44.843 9.515-60.331 25.003-25.003 36.736-25.003 60.331 9.515 44.843 25.003 60.331 36.736 25.003 60.331 25.003 44.843-9.515 60.331-25.003 25.003-36.736 25.003-60.331z" />
<glyph unicode="&#xe924;" glyph-name="file-text" d="M597.333 896h-341.333c-35.328 0-67.413-14.379-90.496-37.504s-37.504-55.168-37.504-90.496v-682.667c0-35.328 14.379-67.413 37.504-90.496s55.168-37.504 90.496-37.504h512c35.328 0 67.413 14.379 90.496 37.504s37.504 55.168 37.504 90.496v512c0 11.776-4.779 22.443-12.501 30.165l-256 256c-4.096 4.096-8.789 7.168-13.824 9.259-5.205 2.176-10.795 3.243-16.341 3.243zM750.336 640h-110.336v110.336zM554.667 810.667v-213.333c0-23.552 19.115-42.667 42.667-42.667h213.333v-469.333c0-11.776-4.736-22.4-12.501-30.165s-18.389-12.501-30.165-12.501h-512c-11.776 0-22.4 4.736-30.165 12.501s-12.501 18.389-12.501 30.165v682.667c0 11.776 4.736 22.4 12.501 30.165s18.389 12.501 30.165 12.501zM682.667 426.667h-341.333c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667h341.333c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667zM682.667 256h-341.333c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667h341.333c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667zM426.667 597.334h-85.333c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667h85.333c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667z" />
<glyph unicode="&#xe9a8;" glyph-name="bidRequest-bidAwarded" d="M1010.174 44.25l-548.634 499.458 25.534 25.598c20.894 20.954 32.188 48.030 33.918 75.61 1.002 0.45 2.002 0.912 2.958 1.442l102.99 64.402c13.934 16.392 12.916 42.268-2.284 57.502l-179.12 179.608c-15.19 15.234-40.998 16.262-57.344 2.284l-64.236-103.268c-0.526-0.966-0.99-1.966-1.44-2.974-27.502-1.736-54.5-13.056-75.398-34.006l-97.428-97.702c-20.898-20.956-32.184-48.026-33.918-75.604-1.004-0.45-2.004-0.916-2.964-1.446l-102.986-64.406c-13.942-16.39-12.916-42.264 2.276-57.496l179.12-179.604c15.194-15.238 40.996-16.262 57.35-2.286l64.228 103.27c0.528 0.958 0.988 1.96 1.442 2.966 27.502 1.738 54.504 13.050 75.398 34.004l28.292 28.372 498.122-550.114c14.436-15.944 36.7-18.518 49.474-5.712l50.356 50.488c12.764 12.808 10.196 35.132-5.706 49.614z" />
<glyph unicode="&#xf00c;" glyph-name="check-fat" d="M954.857 627.428c0-14.286-5.714-28.571-16-38.857l-491.429-491.429c-10.286-10.286-24.571-16-38.857-16s-28.571 5.714-38.857 16l-284.571 284.571c-10.286 10.286-16 24.571-16 38.857s5.714 28.571 16 38.857l77.714 77.714c10.286 10.286 24.571 16 38.857 16s28.571-5.714 38.857-16l168-168.571 374.857 375.429c10.286 10.286 24.571 16 38.857 16s28.571-5.714 38.857-16l77.714-77.714c10.286-10.286 16-24.571 16-38.857z" />
<glyph unicode="&#xf0d7;" glyph-name="sort-down" horiz-adv-x="585" d="M585.143 548.571c0-9.714-4-18.857-10.857-25.714l-256-256c-6.857-6.857-16-10.857-25.714-10.857s-18.857 4-25.714 10.857l-256 256c-6.857 6.857-10.857 16-10.857 25.714 0 20 16.571 36.571 36.571 36.571h512c20 0 36.571-16.571 36.571-36.571z" />
<glyph unicode="&#xf0d8;" glyph-name="sort-up" horiz-adv-x="585" d="M585.143 256c0-20-16.571-36.571-36.571-36.571h-512c-20 0-36.571 16.571-36.571 36.571 0 9.714 4 18.857 10.857 25.714l256 256c6.857 6.857 16 10.857 25.714 10.857s18.857-4 25.714-10.857l256-256c6.857-6.857 10.857-16 10.857-25.714z" />
<glyph unicode="&#xf0dc;" glyph-name="sort" horiz-adv-x="585" d="M585.143 329.143c0-9.714-4-18.857-10.857-25.714l-256-256c-6.857-6.857-16-10.857-25.714-10.857s-18.857 4-25.714 10.857l-256 256c-6.857 6.857-10.857 16-10.857 25.714 0 20 16.571 36.571 36.571 36.571h512c20 0 36.571-16.571 36.571-36.571zM585.143 548.571c0-20-16.571-36.571-36.571-36.571h-512c-20 0-36.571 16.571-36.571 36.571 0 9.714 4 18.857 10.857 25.714l256 256c6.857 6.857 16 10.857 25.714 10.857s18.857-4 25.714-10.857l256-256c6.857-6.857 10.857-16 10.857-25.714z" />
<glyph unicode="&#xf153;" glyph-name="euro" horiz-adv-x="580" d="M557.714 204l20-90.857c2.286-9.143-2.857-18.286-11.429-21.143-2.286-0.571-55.429-18.857-124-18.857-178.286 0-321.714 107.429-369.714 275.429h-54.286c-10.286 0-18.286 8.571-18.286 18.286v64.571c0 9.714 8 18.286 18.286 18.286h37.714c-0.571 18.286-0.571 40 0.571 60h-38.286c-10.286 0-18.286 8-18.286 18.286v65.143c0 10.286 8 18.286 18.286 18.286h56c50.857 160 197.143 266.286 368 266.286 59.429 0 108.571-12.571 110.857-13.143 4.571-1.143 8.571-4.571 11.429-8.571 2.286-4 2.857-9.143 1.714-13.714l-24.571-90.857c-2.286-9.714-12-15.429-21.714-12.571-0.571 0-39.429 9.714-80 9.714-96 0-176.571-52-214.857-137.143h267.429c5.714 0 10.857-2.286 14.286-6.857 3.429-4 5.143-9.714 4-14.857l-13.714-65.143c-1.714-8.571-9.143-14.857-18.286-14.857h-278.857c-1.714-18.286-1.143-37.714 0-60h262.286c5.714 0 10.857-2.857 14.286-6.857 3.429-4.571 4.571-10.286 3.429-15.429l-13.714-64c-1.714-8.571-9.143-14.857-17.714-14.857h-221.143c36.571-89.143 118.857-145.143 216-145.143 49.714 0 90.286 13.714 90.857 13.714 4.571 1.714 10.286 1.143 14.857-1.143 4.571-2.857 7.429-7.429 8.571-12z" />
<glyph unicode="&#xf2b7;" glyph-name="email-open" d="M842.286 429.143l22.286-29.143c6.286-8 4.571-18.857-2.857-25.143-57.143-44.571-188.571-145.714-194.286-150.286-40.571-33.143-95.429-78.857-154.857-78.286h-1.143c-59.429 0-114.286 45.143-154.857 78.286-6.286 5.143-133.143 102.857-189.143 146.286-8 6.286-9.714 17.143-3.429 25.143l21.143 29.714c6.286 8.571 18.286 10.286 26.286 3.429 38.857-30.286 93.714-72.571 174.857-134.857 28.571-21.714 85.143-74.857 125.143-74.857h1.143c40 0 96.571 53.143 125.143 74.857 84 64.571 140 107.429 178.857 138.286 8 6.286 19.429 4.571 25.714-3.429zM950.857 18.286v530.286c-57.143 53.143-48.571 48.571-313.143 253.143-28.571 22.286-85.143 76-125.143 76h-1.143c-40 0-96.571-53.714-125.143-76-264.571-204.571-256-200-313.143-253.143v-530.286c0-9.714 8.571-18.286 18.286-18.286h841.143c9.714 0 18.286 8.571 18.286 18.286zM1024 548.571v-530.286c0-50.286-41.143-91.429-91.429-91.429h-841.143c-50.286 0-91.429 41.143-91.429 91.429v530.286c0 20.571 8.571 40 23.429 53.714 117.143 108.571 252 202.857 333.143 269.714 40 33.143 95.429 78.857 154.857 78.857h1.143c59.429 0 114.857-45.714 154.857-78.857 75.429-62.286 218.857-163.429 333.143-269.714 14.857-13.714 23.429-33.143 23.429-53.714z" />
</font></defs></svg>

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,30 @@
import styled from 'styled-components';
import { font, mixin } from 'shared/utils/styles';
export const Image = styled.div`
display: inline-block;
width: ${props => props.size}px;
height: ${props => props.size}px;
border-radius: 100%;
background-image: url('${props => props.avatarUrl}');
${mixin.backgroundImage}
`;
export const Letter = styled.div`
display: inline-block;
width: ${props => props.size}px;
height: ${props => props.size}px;
border-radius: 100%;
text-transform: uppercase;
color: #fff;
background: ${props => props.color};
${font.medium}
${props => font.size(Math.round(props.size / 1.7))}
& > span {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
`;

View File

@@ -0,0 +1,47 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Image, Letter } from './Styles';
const propTypes = {
className: PropTypes.string,
avatarUrl: PropTypes.string,
name: PropTypes.string,
size: PropTypes.number,
};
const defaultProps = {
className: undefined,
avatarUrl: null,
name: '',
size: 24,
};
const colors = [
'#DA7657',
'#6ADA57',
'#5784DA',
'#AA57DA',
'#DA5757',
'#DA5792',
'#57DACA',
'#57A5DA',
];
const getColorFromName = name => colors[name.toLocaleLowerCase().charCodeAt(0) % colors.length];
const Avatar = ({ className, avatarUrl, name, size }) => {
if (avatarUrl) {
return <Image className={className} size={size} avatarUrl={avatarUrl} />;
}
return (
<Letter className={className} size={size} color={getColorFromName(name)}>
<span>{name.charAt(0)}</span>
</Letter>
);
};
Avatar.propTypes = propTypes;
Avatar.defaultProps = defaultProps;
export default Avatar;

View File

@@ -0,0 +1,86 @@
import styled, { css } from 'styled-components';
import Spinner from 'shared/components/Spinner';
import { color, font, mixin } from 'shared/utils/styles';
export const StyledButton = styled.button`
display: inline-block;
height: 36px;
line-height: 34px;
padding: 0 18px;
vertical-align: middle;
white-space: nowrap;
text-align: center;
border-radius: 4px;
transition: all 0.1s;
appearance: none !important;
${mixin.clickable}
${font.bold}
${font.size(14)}
${props => (props.hollow ? hollowStyles : filledStyles)}
&:disabled {
opacity: 0.6;
cursor: default;
}
i {
position: relative;
top: -1px;
right: 4px;
margin-right: 7px;
display: inline-block;
vertical-align: middle;
line-height: 1;
font-size: 16px;
}
${props => (props.iconOnly ? iconOnlyStyles : '')}
`;
const filledStyles = props => css`
color: #fff;
background: ${color[props.color]};
border: 1px solid ${color[props.color]};
${!props.disabled &&
css`
&:hover,
&:focus {
background: ${mixin.darken(color[props.color], 0.15)};
border: 1px solid ${mixin.darken(color[props.color], 0.15)};
}
&:active {
background: ${mixin.lighten(color[props.color], 0.1)};
border: 1px solid ${mixin.lighten(color[props.color], 0.1)};
}
`}
`;
const hollowStyles = props => css`
color: ${color.textMediumBlue};
background: #fff;
border: 1px solid ${color.borderBlue};
${!props.disabled &&
css`
&:hover,
&:focus {
border: 1px solid ${mixin.darken(color.borderBlue, 0.15)};
}
&:active {
border: 1px solid ${color.borderBlue};
}
`}
`;
const iconOnlyStyles = css`
padding: 0 12px;
i {
right: 0;
margin-right: 0;
}
`;
export const StyledSpinner = styled(Spinner)`
position: relative;
right: 8px;
display: inline-block;
vertical-align: middle;
line-height: 1;
`;

View File

@@ -0,0 +1,67 @@
import React from 'react';
import PropTypes from 'prop-types';
import { color } from 'shared/utils/styles';
import Icon from 'shared/components/Icon';
import { StyledButton, StyledSpinner } from './Styles';
const propTypes = {
className: PropTypes.string,
children: PropTypes.node,
type: PropTypes.string,
hollow: PropTypes.bool,
color: PropTypes.oneOf(['primary', 'success', 'danger']),
icon: PropTypes.string,
iconSize: PropTypes.number,
disabled: PropTypes.bool,
working: PropTypes.bool,
onClick: PropTypes.func,
};
const defaultProps = {
className: undefined,
children: undefined,
type: 'button',
hollow: false,
color: 'primary',
icon: undefined,
iconSize: undefined,
disabled: false,
working: false,
onClick: () => {},
};
const Button = ({
children,
hollow,
icon,
iconSize,
disabled,
working,
onClick = () => {},
...buttonProps
}) => (
<StyledButton
{...buttonProps}
hollow={hollow}
onClick={() => {
if (!disabled && !working) {
onClick();
}
}}
disabled={disabled || working}
working={working}
iconOnly={!children}
>
{working && <StyledSpinner size={26} color={hollow ? color.textMediumBlue : '#fff'} />}
{!working && icon && (
<Icon type={icon} size={iconSize} color={hollow ? color.textMediumBlue : '#fff'} />
)}
{children}
</StyledButton>
);
Button.propTypes = propTypes;
Button.defaultProps = defaultProps;
export default Button;

View File

@@ -0,0 +1,38 @@
import styled from 'styled-components';
import Modal from 'shared/components/Modal';
import Input from 'shared/components/Input';
import Button from 'shared/components/Button';
import { font } from 'shared/utils/styles';
export const StyledConfirmModal = styled(Modal)`
padding: 45px 50px 50px;
`;
export const Title = styled.div`
padding-bottom: 25px;
${font.bold}
${font.size(24)}
line-height: 1.5;
`;
export const Message = styled.p`
padding-bottom: 25px;
white-space: pre-wrap;
${font.size(16)}
`;
export const InputLabel = styled.div`
padding-bottom: 12px;
${font.bold}
${font.size(16)}
`;
export const StyledInput = styled(Input)`
margin-bottom: 25px;
max-width: 220px;
`;
export const StyledButton = styled(Button)`
margin: 5px 20px 0 0;
`;

View File

@@ -0,0 +1,99 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import {
StyledConfirmModal,
Title,
Message,
InputLabel,
StyledInput,
StyledButton,
} from './Styles';
const propTypes = {
className: PropTypes.string,
title: PropTypes.string,
message: PropTypes.oneOfType([PropTypes.string, PropTypes.node]),
confirmText: PropTypes.string,
cancelText: PropTypes.string,
confirmInput: PropTypes.string,
type: PropTypes.oneOf(['primary', 'danger']),
onConfirm: PropTypes.func.isRequired,
renderLink: PropTypes.func.isRequired,
};
const defaultProps = {
className: undefined,
title: 'Warning',
message: 'Are you sure you want to continue with this action?',
confirmText: 'Confirm',
cancelText: 'Cancel',
confirmInput: null,
type: 'primary',
};
const ConfirmModal = ({
className,
title,
message,
confirmText,
cancelText,
confirmInput,
type,
onConfirm,
renderLink,
}) => {
const [isConfirmEnabled, setConfirmEnabled] = useState(false);
const [isWorking, setWorking] = useState(false);
const handleConfirm = modal => {
setWorking(true);
onConfirm({
close: () => {
modal.close();
setWorking(false);
},
});
};
const handleConfirmInputChange = value =>
setConfirmEnabled(value.trim().toLowerCase() === confirmInput.toLowerCase());
return (
<StyledConfirmModal
suppressClassNameWarning
className={className}
afterClose={() => setConfirmEnabled(false)}
renderLink={renderLink}
renderContent={modal => (
<>
<Title>{title}</Title>
{message && <Message>{message}</Message>}
{confirmInput && (
<>
<InputLabel>{`Type ${confirmInput} below to confirm.`}</InputLabel>
<StyledInput onChange={(event, value) => handleConfirmInputChange(value)} />
<br />
</>
)}
<StyledButton hollow onClick={modal.close}>
{cancelText}
</StyledButton>
<StyledButton
color={type}
disabled={confirmInput && !isConfirmEnabled}
working={isWorking}
onClick={() => handleConfirm(modal)}
>
{confirmText}
</StyledButton>
</>
)}
/>
);
};
ConfirmModal.propTypes = propTypes;
ConfirmModal.defaultProps = defaultProps;
export default ConfirmModal;

View File

@@ -0,0 +1,117 @@
import React, { useState } from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { times, range } from 'lodash';
import { formatDate, formatDateTimeForAPI } from 'shared/utils/dateTime';
import Icon from 'shared/components/Icon';
import {
DateSection,
YearSelect,
SelectedMonthYear,
Grid,
PrevNextIcons,
DayName,
Day,
} from './Styles';
const propTypes = {
withTime: PropTypes.bool,
value: PropTypes.string,
onChange: PropTypes.func.isRequired,
setDropdownOpen: PropTypes.func.isRequired,
};
const defaultProps = {
withTime: true,
value: null,
};
const DatePickerDateSection = ({ withTime, value, onChange, setDropdownOpen }) => {
const [selectedMonth, setSelectedMonth] = useState(moment(value || undefined).startOf('month'));
const handleYearChange = year => {
setSelectedMonth(moment(selectedMonth).set({ year: parseInt(year) }));
};
const handleMonthChange = addOrSubtract => {
setSelectedMonth(moment(selectedMonth)[addOrSubtract](1, 'month'));
};
const handleDayChange = newDate => {
const existingHour = value ? moment(value).hour() : '00';
const existingMinute = value ? moment(value).minute() : '00';
const newDateWithExistingTime = newDate.set({
hour: existingHour,
minute: existingMinute,
});
onChange(formatDateTimeForAPI(newDateWithExistingTime));
if (!withTime) {
setDropdownOpen(false);
}
};
const generateYears = () => times(50, i => ({ label: `${i + 2010}`, value: `${i + 2010}` }));
const generateWeekDayNames = () => moment.weekdaysMin(true);
const generateFillerDaysBeforeMonthStart = () => {
const count = selectedMonth.diff(moment(selectedMonth).startOf('week'), 'days');
return range(count);
};
const generateMonthDays = () =>
times(selectedMonth.daysInMonth()).map(i => moment(selectedMonth).add(i, 'days'));
const generateFillerDaysAfterMonthEnd = () => {
const selectedMonthEnd = moment(selectedMonth).endOf('month');
const weekEnd = moment(selectedMonthEnd).endOf('week');
const count = weekEnd.diff(selectedMonthEnd, 'days');
return range(count);
};
return (
<DateSection>
<SelectedMonthYear>{formatDate(selectedMonth, 'MMM YYYY')}</SelectedMonthYear>
<YearSelect onChange={event => handleYearChange(event.target.value)}>
{[{ label: 'Year', value: '' }, ...generateYears()].map(option => (
<option key={option.label} value={option.value}>
{option.label}
</option>
))}
</YearSelect>
<PrevNextIcons>
<Icon type="arrow-left" onClick={() => handleMonthChange('subtract')} />
<Icon type="arrow-right" onClick={() => handleMonthChange('add')} />
</PrevNextIcons>
<Grid>
{generateWeekDayNames().map(name => (
<DayName key={name}>{name}</DayName>
))}
{generateFillerDaysBeforeMonthStart().map(i => (
<Day key={`before-${i}`} isFiller />
))}
{generateMonthDays().map(date => (
<Day
key={date}
isToday={moment().isSame(date, 'day')}
isSelected={moment(value).isSame(date, 'day')}
onClick={() => handleDayChange(date)}
>
{formatDate(date, 'D')}
</Day>
))}
{generateFillerDaysAfterMonthEnd().map(i => (
<Day key={`after-${i}`} isFiller />
))}
</Grid>
</DateSection>
);
};
DatePickerDateSection.propTypes = propTypes;
DatePickerDateSection.defaultProps = defaultProps;
export default DatePickerDateSection;

View File

@@ -0,0 +1,116 @@
import styled from 'styled-components';
import { color, font, mixin, zIndexValues } from 'shared/utils/styles';
export const StyledDatePicker = styled.div`
position: relative;
`;
export const Dropdown = styled.div`
z-index: ${zIndexValues.dropdown};
position: absolute;
top: 130%;
right: 0;
width: 270px;
border-radius: 4px;
background: #fff;
${mixin.boxShadowBorderMedium}
${props => (props.withTime ? withTimeStyles : '')}
`;
const withTimeStyles = `
width: 360px;
padding-right: 90px;
`;
export const DateSection = styled.div`
position: relative;
padding: 20px;
`;
export const SelectedMonthYear = styled.div`
display: inline-block;
padding-left: 7px;
${font.bold}
${font.size(16)}
`;
export const YearSelect = styled.select`
margin-left: 5px;
width: 60px;
height: 22px;
${font.size(13)}
`;
export const PrevNextIcons = styled.div`
position: absolute;
top: 12px;
right: 19px;
i {
padding: 7px 5px 4px;
font-size: 22px;
color: ${color.textLight};
${mixin.clickable}
&:hover {
color: ${color.textDarkest};
}
}
`;
export const Grid = styled.div`
display: flex;
flex-wrap: wrap;
padding-top: 15px;
text-align: center;
`;
export const DayName = styled.div`
width: 14.28%;
height: 30px;
line-height: 30px;
color: ${color.textLight};
${font.size(13)}
`;
export const Day = styled.div`
width: 14.28%;
height: 30px;
line-height: 30px;
border-radius: 4px;
${font.size(15)}
${props => (!props.isFiller ? hoverStyles : '')}
${props => (props.isToday ? font.bold : '')}
${props => (props.isSelected ? selectedStyles : '')}
`;
export const TimeSection = styled.div`
position: absolute;
top: 0;
right: 0;
height: 100%;
width: 90px;
padding: 5px 0;
border-left: 1px solid ${color.borderLight};
${mixin.scrollableY}
`;
export const Time = styled.div`
padding: 5px 0 5px 20px;
${font.size(14)}
${props => (!props.isFiller ? hoverStyles : '')}
${props => (props.isSelected ? selectedStyles : '')}
`;
const hoverStyles = `
${mixin.clickable}
&:hover {
background: ${color.backgroundMedium};
}
`;
const selectedStyles = `
color: #fff;
&:hover, & {
background: ${color.primary};
}
`;

View File

@@ -0,0 +1,76 @@
import React, { useLayoutEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { range } from 'lodash';
import { formatDate, formatDateTimeForAPI } from 'shared/utils/dateTime';
import { TimeSection, Time } from './Styles';
const propTypes = {
value: PropTypes.string,
onChange: PropTypes.func.isRequired,
setDropdownOpen: PropTypes.func.isRequired,
};
const defaultProps = {
value: null,
};
const DatePickerTimeSection = ({ value, onChange, setDropdownOpen }) => {
const $sectionRef = useRef();
const formattedTimeValue = formatDate(value, 'HH:mm');
useLayoutEffect(() => {
const scrollToSelectedTime = () => {
if (!$sectionRef.current) return;
const $selectedTime = $sectionRef.current.querySelector(
`[data-time="${formattedTimeValue}"]`,
);
if (!$selectedTime) return;
$sectionRef.current.scrollTop = $selectedTime.offsetTop - 80;
};
scrollToSelectedTime();
}, [formattedTimeValue]);
const handleTimeChange = newTime => {
const [newHour, newMinute] = newTime.split(':');
const existingDate = moment(value || undefined);
const existingDateWithNewTime = existingDate.set({
hour: parseInt(newHour),
minute: parseInt(newMinute),
});
onChange(formatDateTimeForAPI(existingDateWithNewTime));
setDropdownOpen(false);
};
const generateTimes = () =>
range(48).map(i => {
const hour = `${Math.floor(i / 2)}`;
const paddedHour = hour.length < 2 ? `0${hour}` : hour;
const minute = i % 2 === 0 ? '00' : '30';
return `${paddedHour}:${minute}`;
});
return (
<TimeSection ref={$sectionRef}>
{generateTimes().map(time => (
<Time
key={time}
data-time={time}
isSelected={time === formattedTimeValue}
onClick={() => handleTimeChange(time)}
>
{time}
</Time>
))}
</TimeSection>
);
};
DatePickerTimeSection.propTypes = propTypes;
DatePickerTimeSection.defaultProps = defaultProps;
export default DatePickerTimeSection;

View File

@@ -0,0 +1,65 @@
import React, { useState, useRef } from 'react';
import PropTypes from 'prop-types';
import { formatDate, formatDateTime } from 'shared/utils/dateTime';
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
import Input from 'shared/components/Input';
import DateSection from './DateSection';
import TimeSection from './TimeSection';
import { StyledDatePicker, Dropdown } from './Styles';
const propTypes = {
className: PropTypes.string,
withTime: PropTypes.bool,
value: PropTypes.string,
onChange: PropTypes.func.isRequired,
};
const defaultProps = {
className: undefined,
withTime: true,
value: null,
};
const DatePicker = ({ className, withTime, value, onChange, ...inputProps }) => {
const [isDropdownOpen, setDropdownOpen] = useState(false);
const $containerRef = useRef();
useOnOutsideClick($containerRef, isDropdownOpen, () => setDropdownOpen(false));
const formatValueForInput = () => {
if (!value) return '';
return withTime ? formatDateTime(value) : formatDate(value);
};
return (
<StyledDatePicker ref={$containerRef}>
<Input
icon="calendar"
{...inputProps}
className={className}
autoComplete="off"
value={formatValueForInput()}
onClick={() => setDropdownOpen(true)}
/>
{isDropdownOpen && (
<Dropdown withTime={withTime}>
<DateSection
withTime={withTime}
value={value}
onChange={onChange}
setDropdownOpen={setDropdownOpen}
/>
{withTime && (
<TimeSection value={value} onChange={onChange} setDropdownOpen={setDropdownOpen} />
)}
</Dropdown>
)}
</StyledDatePicker>
);
};
DatePicker.propTypes = propTypes;
DatePicker.defaultProps = defaultProps;
export default DatePicker;

View File

@@ -0,0 +1,20 @@
import styled from 'styled-components';
export default styled.i`
display: inline-block;
font-size: ${props => `${props.size}px`};
${props =>
props.left || props.top ? `transform: translate(${props.left}px, ${props.top}px);` : ''}
&:before {
content: "${props => props.code}";
font-family: "jira" !important;
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
`;

View File

@@ -0,0 +1,65 @@
import React from 'react';
import PropTypes from 'prop-types';
import StyledIcon from './Styles';
const codes = {
[`check-circle`]: '\\e86c',
[`check-fat`]: '\\f00c',
[`arrow-left`]: '\\e900',
[`arrow-right`]: '\\e912',
[`upload-thin`]: '\\e91f',
[`bell`]: '\\e901',
[`calendar`]: '\\e903',
[`check`]: '\\e904',
[`chevron-down`]: '\\e905',
[`chevron-left`]: '\\e906',
[`chevron-right`]: '\\e907',
[`chevron-up`]: '\\e908',
[`clock`]: '\\e909',
[`download`]: '\\e90a',
[`plus`]: '\\e90c',
[`refresh`]: '\\e90d',
[`search`]: '\\e90e',
[`upload`]: '\\e90f',
[`close`]: '\\e910',
[`archive`]: '\\e915',
[`briefcase`]: '\\e916',
[`settings`]: '\\e902',
[`email`]: '\\e914',
[`lock`]: '\\e913',
[`dashboard`]: '\\e917',
[`alert`]: '\\e911',
[`edit`]: '\\e918',
[`delete`]: '\\e919',
[`sort`]: '\\f0dc',
[`sort-up`]: '\\f0d8',
[`sort-down`]: '\\f0d7',
[`euro`]: '\\f153',
[`folder-plus`]: '\\e921',
[`folder-minus`]: '\\e920',
[`file`]: '\\e90b',
[`file-text`]: '\\e924',
};
const propTypes = {
type: PropTypes.oneOf(Object.keys(codes)).isRequired,
className: PropTypes.string,
size: PropTypes.number,
left: PropTypes.number,
top: PropTypes.number,
};
const defaultProps = {
className: undefined,
size: 16,
left: 0,
top: 0,
};
const Icon = ({ type, ...iconProps }) => <StyledIcon {...iconProps} code={codes[type]} />;
Icon.propTypes = propTypes;
Icon.defaultProps = defaultProps;
export default Icon;

View File

@@ -0,0 +1,34 @@
import styled from 'styled-components';
import { color, font } from 'shared/utils/styles';
export default styled.div`
position: relative;
display: inline-block;
height: 40px;
width: 100%;
input {
height: 100%;
width: 100%;
padding: 0 15px;
border-radius: 4px;
border: 1px solid ${color.borderLight};
box-shadow: inset 0 0 1px 0 rgba(0, 0, 0, 0.03);
background: #fff;
${font.regular}
${font.size(14)}
&:focus {
border: 1px solid ${color.borderMedium};
}
${props => (props.icon ? 'padding-left: 40px;' : '')}
${props => (props.invalid ? `&, &:focus { border: 1px solid ${color.danger}; }` : '')}
}
i {
position: absolute;
top: 12px;
left: 14px;
font-size: 16px;
pointer-events: none;
color: ${color.textMedium};
}
`;

View File

@@ -0,0 +1,42 @@
import React, { forwardRef } from 'react';
import PropTypes from 'prop-types';
import Icon from 'shared/components/Icon';
import StyledInput from './Styles';
const propTypes = {
icon: PropTypes.string,
className: PropTypes.string,
invalid: PropTypes.bool,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
filter: PropTypes.instanceOf(RegExp),
onChange: PropTypes.func,
};
const defaultProps = {
icon: undefined,
className: undefined,
invalid: false,
value: undefined,
filter: undefined,
onChange: () => {},
};
const Input = forwardRef(({ icon, className, invalid, filter, onChange, ...inputProps }, ref) => {
const handleChange = event => {
if (!filter || filter.test(event.target.value)) {
onChange(event, event.target.value);
}
};
return (
<StyledInput className={className} icon={icon} invalid={invalid}>
{icon && <Icon type={icon} />}
<input {...inputProps} onChange={handleChange} ref={ref} />
</StyledInput>
);
});
Input.propTypes = propTypes;
Input.defaultProps = defaultProps;
export default Input;

View File

@@ -0,0 +1,62 @@
import React from 'react';
import PropTypes from 'prop-types';
const propTypes = {
className: PropTypes.string,
width: PropTypes.number,
};
const defaultProps = {
className: undefined,
width: 28,
};
const Logo = ({ className, width }) => (
<span className={className}>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 75.76 75.76" width={width}>
<defs>
<linearGradient
id="linear-gradient"
x1="34.64"
y1="15.35"
x2="19"
y2="30.99"
gradientUnits="userSpaceOnUse"
>
<stop offset="0.18" stopColor="rgba(0, 82, 204, 0.2)" />
<stop offset="1" stopColor="#DEEBFE" />
</linearGradient>
<linearGradient
id="linear-gradient-2"
x1="38.78"
y1="60.28"
x2="54.39"
y2="44.67"
xlinkHref="#linear-gradient"
/>
</defs>
<title>Jira Software-blue</title>
<g id="Layer_2" data-name="Layer 2">
<g id="Blue">
<path
style={{ fill: '#DEEBFE' }}
d="M72.4,35.76,39.8,3.16,36.64,0h0L12.1,24.54h0L.88,35.76A3,3,0,0,0,.88,40L23.3,62.42,36.64,75.76,61.18,51.22l.38-.38L72.4,40A3,3,0,0,0,72.4,35.76ZM36.64,49.08l-11.2-11.2,11.2-11.2,11.2,11.2Z"
/>
<path
style={{ fill: 'url(#linear-gradient)' }}
d="M36.64,26.68A18.86,18.86,0,0,1,36.56.09L12.05,24.59,25.39,37.93,36.64,26.68Z"
/>
<path
style={{ fill: 'url(#linear-gradient-2)' }}
d="M47.87,37.85,36.64,49.08a18.86,18.86,0,0,1,0,26.68h0L61.21,51.19Z"
/>
</g>
</g>
</svg>
</span>
);
Logo.propTypes = propTypes;
Logo.defaultProps = defaultProps;
export default Logo;

View File

@@ -0,0 +1,82 @@
import styled, { css } from 'styled-components';
import Icon from 'shared/components/Icon';
import { color, mixin, zIndexValues } from 'shared/utils/styles';
export const ScrollOverlay = styled.div`
z-index: ${zIndexValues.modal};
position: fixed;
top: 0;
left: 0;
height: 100%;
width: 100%;
${mixin.scrollableY}
`;
export const ClickableOverlay = styled.div`
min-height: 100%;
background: ${mixin.rgba(color.textLightBlue, 0.7)};
${props => clickOverlayStyles[props.variant]}
`;
const clickOverlayStyles = {
center: css`
display: flex;
justify-content: center;
align-items: center;
padding: 50px;
`,
aside: css`
text-align: right;
`,
};
export const StyledModal = styled.div`
display: inline-block;
position: relative;
width: 100%;
background: #fff;
${props => modalStyles[props.variant]}
`;
const modalStyles = {
center: css`
max-width: 600px;
vertical-align: middle;
text-align: left;
${mixin.boxShadowMedium}
`,
aside: css`
min-height: 100vh;
max-width: 500px;
text-align: left;
box-shadow: 0 0 20px 0 rgba(0, 0, 0, 0.15);
`,
};
export const CloseIcon = styled(Icon)`
position: absolute;
font-size: 25px;
color: ${color.textDark};
${mixin.clickable}
${props => closeIconStyles[props.variant]}
`;
const closeIconStyles = {
center: css`
top: 8px;
right: 10px;
padding: 7px 7px 0;
`,
aside: css`
top: 10px;
left: -50px;
width: 40px;
height: 40px;
padding-top: 8px;
border-radius: 40px;
text-align: center;
background: #fff;
opacity: 0.5;
`,
};

View File

@@ -0,0 +1,94 @@
import React, { useState, useRef, useEffect, useCallback } from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import { uniqueId as uniqueIncreasingIntegerId } from 'lodash';
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
import useOnEscapeKeyDown from 'shared/hooks/onEscapeKeyDown';
import { ScrollOverlay, ClickableOverlay, StyledModal, CloseIcon } from './Styles';
const propTypes = {
className: PropTypes.string,
variant: PropTypes.oneOf(['center', 'aside']),
isOpen: PropTypes.bool,
onClose: PropTypes.func,
renderLink: PropTypes.func,
renderContent: PropTypes.func.isRequired,
};
const defaultProps = {
className: undefined,
variant: 'center',
isOpen: undefined,
onClose: () => {},
renderLink: () => {},
};
const Modal = ({
className,
variant,
isOpen: propsIsOpen,
onClose: tellParentToClose,
renderLink,
renderContent,
}) => {
const [stateIsOpen, setStateOpen] = useState(false);
const isControlled = typeof propsIsOpen === 'boolean';
const isOpen = isControlled ? propsIsOpen : stateIsOpen;
const $modalRef = useRef();
const modalIdRef = useRef(uniqueIncreasingIntegerId());
const closeModal = useCallback(() => {
if (shouldNotCloseBecauseHasOpenChildModal(modalIdRef.current)) {
return;
}
if (!isControlled) {
setStateOpen(false);
} else {
tellParentToClose();
}
}, [isControlled, tellParentToClose]);
useOnOutsideClick($modalRef, isOpen, closeModal);
useOnEscapeKeyDown(isOpen, closeModal);
useEffect(setBodyScrollLock, [isOpen]);
const renderModal = () => (
<ScrollOverlay data-jira-modal-id={modalIdRef.current}>
<ClickableOverlay variant={variant}>
<StyledModal className={className} variant={variant} ref={$modalRef}>
<CloseIcon type="close" variant={variant} onClick={closeModal} />
{renderContent({ close: closeModal })}
</StyledModal>
</ClickableOverlay>
</ScrollOverlay>
);
return (
<>
{!isControlled && renderLink({ open: () => setStateOpen(true) })}
{isOpen && ReactDOM.createPortal(renderModal(), $root)}
</>
);
};
const $root = document.getElementById('root');
const getIdsOfAllOpenModals = () => {
const $modalNodes = Array.from(document.querySelectorAll('[data-jira-modal-id]'));
return $modalNodes.map($node => parseInt($node.getAttribute('data-jira-modal-id')));
};
const shouldNotCloseBecauseHasOpenChildModal = modalId =>
getIdsOfAllOpenModals().some(id => id > modalId);
const setBodyScrollLock = () => {
const areAnyModalsOpen = getIdsOfAllOpenModals().length > 0;
document.body.style.overflow = areAnyModalsOpen ? 'hidden' : 'visible';
};
Modal.propTypes = propTypes;
Modal.defaultProps = defaultProps;
export default Modal;

View File

@@ -0,0 +1,7 @@
import styled from 'styled-components';
export default styled.div`
width: 100%;
padding: 100px;
text-align: center;
`;

View File

@@ -0,0 +1,12 @@
import React from 'react';
import Spinner from 'shared/components/Spinner';
import StyledPageLoader from './Styles';
const PageLoader = () => (
<StyledPageLoader>
<Spinner size={70} />
</StyledPageLoader>
);
export default PageLoader;

View File

@@ -0,0 +1,204 @@
import React, { useState, useRef } from 'react';
import PropTypes from 'prop-types';
import { uniq } from 'lodash';
import { KeyCodes } from 'shared/constants/keyCodes';
import { ClearIcon, Dropdown, DropdownInput, Options, Option, OptionsNoResults } from './Styles';
const propTypes = {
value: PropTypes.any,
isValueEmpty: PropTypes.bool.isRequired,
searchValue: PropTypes.string.isRequired,
setSearchValue: PropTypes.func.isRequired,
$inputRef: PropTypes.object.isRequired,
deactivateDropdown: PropTypes.func.isRequired,
options: PropTypes.array.isRequired,
onChange: PropTypes.func.isRequired,
onCreate: PropTypes.func,
isMulti: PropTypes.bool,
};
const defaultProps = {
value: undefined,
onCreate: undefined,
isMulti: false,
};
const SelectDropdown = ({
value,
isValueEmpty,
searchValue,
setSearchValue,
$inputRef,
deactivateDropdown,
options,
onChange,
onCreate,
isMulti,
}) => {
const [isCreatingOption, setCreatingOption] = useState(false);
const $optionsRef = useRef();
const selectOptionValue = optionValue => {
deactivateDropdown();
if (isMulti) {
onChange(uniq([...value, optionValue]));
} else {
onChange(optionValue);
}
};
const createOption = newOptionLabel => {
setCreatingOption(true);
onCreate(newOptionLabel, createdOptionValue => {
setCreatingOption(false);
selectOptionValue(createdOptionValue);
});
};
const clearOptionValues = () => {
$inputRef.current.value = '';
$inputRef.current.focus();
onChange(isMulti ? [] : null);
};
const handleInputKeyDown = event => {
if (event.keyCode === KeyCodes.escape) {
handleInputEscapeKeyDown(event);
} else if (event.keyCode === KeyCodes.enter) {
handleInputEnterKeyDown(event);
} else if (event.keyCode === KeyCodes.arrowDown || event.keyCode === KeyCodes.arrowUp) {
handleInputArrowUpOrDownKeyDown(event);
}
};
const handleInputEscapeKeyDown = event => {
event.nativeEvent.stopImmediatePropagation();
deactivateDropdown();
};
const handleInputEnterKeyDown = event => {
event.preventDefault();
const $active = getActiveOptionNode();
if (!$active) return;
const optionValueToSelect = $active.getAttribute('data-select-option-value');
const optionLabelToCreate = $active.getAttribute('data-create-option-label');
if (optionValueToSelect) {
selectOptionValue(optionValueToSelect);
} else if (optionLabelToCreate) {
createOption(optionLabelToCreate);
}
};
const handleInputArrowUpOrDownKeyDown = event => {
const $active = getActiveOptionNode();
if (!$active) return;
const $options = $optionsRef.current;
const $optionsHeight = $options.getBoundingClientRect().height;
const $activeHeight = $active.getBoundingClientRect().height;
if (event.keyCode === KeyCodes.arrowDown) {
if ($options.lastElementChild === $active) {
$active.classList.remove(activeOptionClass);
$options.firstElementChild.classList.add(activeOptionClass);
$options.scrollTop = 0;
} else {
$active.classList.remove(activeOptionClass);
$active.nextElementSibling.classList.add(activeOptionClass);
if ($active.offsetTop > $options.scrollTop + $optionsHeight / 1.4) {
$options.scrollTop += $activeHeight;
}
}
} else if (event.keyCode === KeyCodes.arrowUp) {
if ($options.firstElementChild === $active) {
$active.classList.remove(activeOptionClass);
$options.lastElementChild.classList.add(activeOptionClass);
$options.scrollTop = $options.scrollHeight;
} else {
$active.classList.remove(activeOptionClass);
$active.previousElementSibling.classList.add(activeOptionClass);
if ($active.offsetTop < $options.scrollTop + $optionsHeight / 2.4) {
$options.scrollTop -= $activeHeight;
}
}
}
};
const handleOptionMouseEnter = event => {
const $active = getActiveOptionNode();
if ($active) $active.classList.remove(activeOptionClass);
event.target.classList.add(activeOptionClass);
};
const getActiveOptionNode = () => $optionsRef.current.querySelector(`.${activeOptionClass}`);
const optionsFilteredBySearchValue = options.filter(option =>
option.label
.toString()
.toLowerCase()
.includes(searchValue.toLowerCase()),
);
const removeSelectedOptions = opts => opts.filter(option => !value.includes(option.value));
const filteredOptions = isMulti
? removeSelectedOptions(optionsFilteredBySearchValue)
: optionsFilteredBySearchValue;
const searchValueNotInOptions = !options.map(option => option.label).includes(searchValue);
const isOptionCreatable = onCreate && searchValue && searchValueNotInOptions;
const renderSelectableOption = (option, i) => (
<Option
key={option.value}
className={i === 0 ? activeOptionClass : undefined}
isSelected={option.value === value}
data-select-option-value={option.value}
onMouseEnter={handleOptionMouseEnter}
onClick={() => selectOptionValue(option.value)}
>
{option.label}
</Option>
);
const renderCreatableOption = () => (
<Option
className={filteredOptions.length === 0 ? activeOptionClass : undefined}
data-create-option-label={searchValue}
onMouseEnter={handleOptionMouseEnter}
onClick={() => createOption(searchValue)}
>
{isCreatingOption ? `Creating "${searchValue}"...` : `Create "${searchValue}"`}
</Option>
);
return (
<Dropdown>
<DropdownInput
type="text"
placeholder="Search"
ref={$inputRef}
autoFocus
onKeyDown={handleInputKeyDown}
onChange={event => setSearchValue(event.target.value)}
/>
{!isValueEmpty && <ClearIcon type="close" onClick={clearOptionValues} />}
<Options ref={$optionsRef}>
{filteredOptions.map(renderSelectableOption)}
{isOptionCreatable && renderCreatableOption()}
{filteredOptions.length === 0 && <OptionsNoResults>No results</OptionsNoResults>}
</Options>
</Dropdown>
);
};
const activeOptionClass = 'jira-select-option-is-active';
SelectDropdown.propTypes = propTypes;
SelectDropdown.defaultProps = defaultProps;
export default SelectDropdown;

View File

@@ -0,0 +1,134 @@
import styled, { css } from 'styled-components';
import { color, font, mixin, zIndexValues } from 'shared/utils/styles';
import Icon from 'shared/components/Icon';
export const StyledSelect = styled.div`
position: relative;
width: 100%;
border-radius: 4px;
border: 1px solid ${color.borderLight};
background: #fff;
${font.size(14)}
&:focus {
outline: none;
border: 1px solid ${color.borderMedium};
}
${props => (props.hasIcon ? 'padding-left: 25px;' : '')}
${props => (props.invalid ? `&, &:focus { border: 1px solid ${color.danger}; }` : '')}
`;
export const StyledIcon = styled(Icon)`
position: absolute;
top: 12px;
left: 14px;
font-size: 16px;
color: ${color.textMedium};
`;
export const ValueContainer = styled.div`
min-height: 38px;
width: 100%;
`;
export const ChevronIcon = styled(Icon)`
position: absolute;
top: 10px;
right: 11px;
font-size: 18px;
color: ${color.textMedium};
`;
export const Placeholder = styled.div`
padding: 11px 0 0 15px;
color: ${color.textLightBlue};
`;
export const ValueSingle = styled.div`
padding: 11px 0 0 15px;
`;
export const ValueMulti = styled.div`
padding: 15px 5px 10px 10px;
`;
export const ValueMultiItem = styled.div`
margin: 0 5px 5px 0;
${mixin.tag}
`;
export const AddMore = styled.div`
display: inline-block;
height: 24px;
line-height: 22px;
padding-right: 5px;
${font.size(12)}
${mixin.link()}
i {
margin-right: 3px;
vertical-align: middle;
font-size: 14px;
}
`;
export const Dropdown = styled.div`
z-index: ${zIndexValues.dropdown};
position: absolute;
top: 100%;
left: 0;
width: 100%;
background: #fff;
${mixin.boxShadowBorderMedium}
`;
export const DropdownInput = styled.input`
padding: 10px 15px 8px;
width: 100%;
border: none;
color: ${color.textDarkest};
background: none;
&:focus {
outline: none;
}
`;
export const ClearIcon = styled(Icon)`
position: absolute;
top: 4px;
right: 7px;
padding: 5px;
font-size: 16px;
color: ${color.textMedium};
${mixin.clickable}
`;
export const Options = styled.div`
max-height: 200px;
${mixin.scrollableY};
${mixin.customScrollbar()};
`;
export const Option = styled.div`
padding: 5px 15px;
word-break: break-word;
&:hover {
cursor: pointer;
}
&:last-of-type {
margin-bottom: 8px;
}
&.jira-select-option-is-active {
background: ${mixin.lighten(color.backgroundMedium, 0.05)};
}
${props => (props.isSelected ? selectedOptionStyles : '')}
`;
const selectedOptionStyles = css`
color: #fff !important;
background: ${color.primary} !important;
`;
export const OptionsNoResults = styled.div`
padding: 5px 15px 15px;
color: ${color.textLight};
`;

View File

@@ -0,0 +1,164 @@
import React, { useState, useRef } from 'react';
import PropTypes from 'prop-types';
import useOnOutsideClick from 'shared/hooks/onOutsideClick';
import { KeyCodes } from 'shared/constants/keyCodes';
import Icon from 'shared/components/Icon';
import Dropdown from './Dropdown';
import {
StyledSelect,
StyledIcon,
ValueContainer,
ChevronIcon,
Placeholder,
ValueSingle,
ValueMulti,
ValueMultiItem,
AddMore,
} from './Styles';
const propTypes = {
className: PropTypes.string,
icon: PropTypes.string,
value: PropTypes.any,
defaultValue: PropTypes.any,
placeholder: PropTypes.string,
invalid: PropTypes.bool,
options: PropTypes.array.isRequired,
onChange: PropTypes.func.isRequired,
onCreate: PropTypes.func,
isMulti: PropTypes.bool,
};
const defaultProps = {
className: undefined,
icon: undefined,
value: undefined,
defaultValue: undefined,
placeholder: '',
invalid: false,
onCreate: undefined,
isMulti: false,
};
const Select = ({
className,
icon,
value: propsValue,
defaultValue,
placeholder,
invalid,
options,
onChange,
onCreate,
isMulti,
}) => {
const [stateValue, setStateValue] = useState(defaultValue || (isMulti ? [] : null));
const [isDropdownOpen, setDropdownOpen] = useState(false);
const [searchValue, setSearchValue] = useState('');
const isControlled = propsValue !== undefined;
const value = isControlled ? propsValue : stateValue;
const $selectRef = useRef();
const $inputRef = useRef();
const activateDropdown = () => {
if (isDropdownOpen) {
$inputRef.current.focus();
} else {
setDropdownOpen(true);
}
};
const deactivateDropdown = () => {
setDropdownOpen(false);
setSearchValue('');
$selectRef.current.focus();
};
useOnOutsideClick($selectRef, isDropdownOpen, deactivateDropdown);
const handleChange = newValue => {
if (!isControlled) {
setStateValue(newValue);
}
onChange(newValue);
};
const removeOptionValue = optionValue => {
handleChange(value.filter(val => val !== optionValue));
};
const handleFocusedSelectKeydown = event => {
if (isDropdownOpen) return;
if (event.keyCode === KeyCodes.enter) {
event.preventDefault();
}
if (event.keyCode !== KeyCodes.escape && event.keyCode !== KeyCodes.tab && !event.shiftKey) {
setDropdownOpen(true);
}
};
const getOption = optionValue => options.find(option => option.value === optionValue);
const getOptionLabel = optionValue => (getOption(optionValue) || { label: '' }).label;
const isValueEmpty = isMulti ? !value.length : !getOption(value);
const renderSingleValue = () => <ValueSingle>{getOptionLabel(value)}</ValueSingle>;
const renderMultiValue = () => (
<ValueMulti>
{value.map(optionValue => (
<ValueMultiItem key={optionValue} onClick={() => removeOptionValue(optionValue)}>
{getOptionLabel(optionValue)}
<Icon type="close" />
</ValueMultiItem>
))}
<AddMore>
<Icon type="plus" />
Add more
</AddMore>
</ValueMulti>
);
return (
<StyledSelect
className={className}
ref={$selectRef}
tabIndex="0"
hasIcon={!!icon}
onKeyDown={handleFocusedSelectKeydown}
invalid={invalid}
>
<ValueContainer onClick={activateDropdown}>
{icon && <StyledIcon type={icon} />}
{(!isMulti || isValueEmpty) && <ChevronIcon type="chevron-down" />}
{isValueEmpty && <Placeholder>{placeholder}</Placeholder>}
{!isValueEmpty && !isMulti && renderSingleValue()}
{!isValueEmpty && isMulti && renderMultiValue()}
</ValueContainer>
{isDropdownOpen && (
<Dropdown
value={value}
isValueEmpty={isValueEmpty}
searchValue={searchValue}
setSearchValue={setSearchValue}
$selectRef={$selectRef}
$inputRef={$inputRef}
deactivateDropdown={deactivateDropdown}
options={options}
onChange={handleChange}
onCreate={onCreate}
isMulti={isMulti}
/>
)}
</StyledSelect>
);
};
Select.propTypes = propTypes;
Select.defaultProps = defaultProps;
export default Select;

View File

@@ -0,0 +1,219 @@
import React from 'react';
import PropTypes from 'prop-types';
import { color as colors } from 'shared/utils/styles';
const propTypes = {
className: PropTypes.string,
size: PropTypes.number,
color: PropTypes.string,
};
const defaultProps = {
className: undefined,
size: 32,
color: colors.textMedium,
};
const Spinner = ({ className, size, color }) => (
<span className={className}>
<svg
width={size}
height={size}
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 100 100"
preserveAspectRatio="xMidYMid"
style={{ background: '0 0' }}
>
<g>
<g transform="translate(80 50)">
<circle r={8} fill={color}>
<animateTransform
attributeName="transform"
type="scale"
begin="-0.875s"
values="1 1;1 1"
keyTimes="0;1"
dur="1s"
repeatCount="indefinite"
/>
<animate
attributeName="fill-opacity"
keyTimes="0;1"
dur="1s"
repeatCount="indefinite"
values="1;0"
begin="-0.875s"
/>
</circle>
</g>
</g>
<g>
<g transform="rotate(45 -50.355 121.569)">
<circle r={8} fill={color} fillOpacity=".875">
<animateTransform
attributeName="transform"
type="scale"
begin="-0.75s"
values="1 1;1 1"
keyTimes="0;1"
dur="1s"
repeatCount="indefinite"
/>
<animate
attributeName="fill-opacity"
keyTimes="0;1"
dur="1s"
repeatCount="indefinite"
values="1;0"
begin="-0.75s"
/>
</circle>
</g>
</g>
<g>
<g transform="rotate(90 -15 65)">
<circle r={8} fill={color} fillOpacity=".75">
<animateTransform
attributeName="transform"
type="scale"
begin="-0.625s"
values="1 1;1 1"
keyTimes="0;1"
dur="1s"
repeatCount="indefinite"
/>
<animate
attributeName="fill-opacity"
keyTimes="0;1"
dur="1s"
repeatCount="indefinite"
values="1;0"
begin="-0.625s"
/>
</circle>
</g>
</g>
<g>
<g transform="rotate(135 -.355 41.569)">
<circle r={8} fill={color} fillOpacity=".625">
<animateTransform
attributeName="transform"
type="scale"
begin="-0.5s"
values="1 1;1 1"
keyTimes="0;1"
dur="1s"
repeatCount="indefinite"
/>
<animate
attributeName="fill-opacity"
keyTimes="0;1"
dur="1s"
repeatCount="indefinite"
values="1;0"
begin="-0.5s"
/>
</circle>
</g>
</g>
<g>
<g transform="rotate(180 10 25)">
<circle r={8} fill={color} fillOpacity=".5">
<animateTransform
attributeName="transform"
type="scale"
begin="-0.375s"
values="1 1;1 1"
keyTimes="0;1"
dur="1s"
repeatCount="indefinite"
/>
<animate
attributeName="fill-opacity"
keyTimes="0;1"
dur="1s"
repeatCount="indefinite"
values="1;0"
begin="-0.375s"
/>
</circle>
</g>
</g>
<g>
<g transform="rotate(-135 20.355 8.431)">
<circle r={8} fill={color} fillOpacity=".375">
<animateTransform
attributeName="transform"
type="scale"
begin="-0.25s"
values="1 1;1 1"
keyTimes="0;1"
dur="1s"
repeatCount="indefinite"
/>
<animate
attributeName="fill-opacity"
keyTimes="0;1"
dur="1s"
repeatCount="indefinite"
values="1;0"
begin="-0.25s"
/>
</circle>
</g>
</g>
<g>
<g transform="rotate(-90 35 -15)">
<circle r={8} fill={color} fillOpacity=".25">
<animateTransform
attributeName="transform"
type="scale"
begin="-0.125s"
values="1 1;1 1"
keyTimes="0;1"
dur="1s"
repeatCount="indefinite"
/>
<animate
attributeName="fill-opacity"
keyTimes="0;1"
dur="1s"
repeatCount="indefinite"
values="1;0"
begin="-0.125s"
/>
</circle>
</g>
</g>
<g>
<g transform="rotate(-45 70.355 -71.569)">
<circle r={8} fill={color} fillOpacity=".125">
<animateTransform
attributeName="transform"
type="scale"
begin="0s"
values="1 1;1 1"
keyTimes="0;1"
dur="1s"
repeatCount="indefinite"
/>
<animate
attributeName="fill-opacity"
keyTimes="0;1"
dur="1s"
repeatCount="indefinite"
values="1;0"
begin="0s"
/>
</circle>
</g>
</g>
</svg>
</span>
);
Spinner.propTypes = propTypes;
Spinner.defaultProps = defaultProps;
export default Spinner;

View File

@@ -0,0 +1,23 @@
import styled from 'styled-components';
import { color, font } from 'shared/utils/styles';
export default styled.div`
display: inline-block;
width: 100%;
textarea {
width: 100%;
padding: 13px 15px 14px;
border-radius: 4px;
border: 1px solid ${color.borderLight};
box-shadow: inset 0 0 1px 0 rgba(0, 0, 0, 0.03);
background: #fff;
overflow-y: hidden;
${font.regular}
${font.size(14)}
&:focus {
border: 1px solid ${color.borderMedium};
}
${props => (props.invalid ? `&, &:focus { border: 1px solid ${color.danger}; }` : '')}
}
`;

View File

@@ -0,0 +1,36 @@
import React, { forwardRef } from 'react';
import PropTypes from 'prop-types';
import TextareaAutoSize from 'react-textarea-autosize';
import StyledTextarea from './Styles';
const propTypes = {
className: PropTypes.string,
invalid: PropTypes.bool,
minRows: PropTypes.number,
value: PropTypes.string,
onChange: PropTypes.func,
};
const defaultProps = {
className: undefined,
invalid: false,
minRows: 2,
value: undefined,
onChange: () => {},
};
const Textarea = forwardRef(({ className, invalid, onChange, ...textareaProps }, ref) => (
<StyledTextarea className={className} invalid={invalid}>
<TextareaAutoSize
{...textareaProps}
onChange={event => onChange(event, event.target.value)}
ref={ref}
/>
</StyledTextarea>
));
Textarea.propTypes = propTypes;
Textarea.defaultProps = defaultProps;
export default Textarea;

View File

@@ -0,0 +1,12 @@
export { default as Avatar } from './Avatar';
export { default as Button } from './Button';
export { default as ConfirmModal } from './ConfirmModal';
export { default as DatePicker } from './DatePicker';
export { default as Icon } from './Icon';
export { default as Input } from './Input';
export { default as Logo } from './Logo';
export { default as Modal } from './Modal';
export { default as PageLoader } from './PageLoader';
export { default as Select } from './Select';
export { default as Spinner } from './Spinner';
export { default as Textarea } from './Textarea';

View File

@@ -0,0 +1,7 @@
export const KeyCodes = {
escape: 27,
tab: 9,
enter: 13,
arrowUp: 38,
arrowDown: 40,
};

View File

@@ -0,0 +1,21 @@
import { useEffect } from 'react';
import { KeyCodes } from 'shared/constants/keyCodes';
const useOnEscapeKeyDown = (isListening, onEscapeKeyDown) => {
useEffect(() => {
const handleKeyDown = event => {
if (event.keyCode === KeyCodes.escape) {
onEscapeKeyDown();
}
};
if (isListening) {
document.addEventListener('keydown', handleKeyDown);
}
return () => {
document.removeEventListener('keydown', handleKeyDown);
};
}, [isListening, onEscapeKeyDown]);
};
export default useOnEscapeKeyDown;

View File

@@ -0,0 +1,30 @@
import { useEffect, useRef } from 'react';
const useOnOutsideClick = ($elementRef, isListening, onOutsideClick) => {
const $mouseDownTargetRef = useRef();
useEffect(() => {
const handleMouseDown = event => {
$mouseDownTargetRef.current = event.target;
};
const handleMouseUp = event => {
if (
event.button === 0 &&
!$elementRef.current.contains($mouseDownTargetRef.current) &&
!$elementRef.current.contains(event.target)
) {
onOutsideClick();
}
};
if (isListening) {
document.addEventListener('mousedown', handleMouseDown);
document.addEventListener('mouseup', handleMouseUp);
}
return () => {
document.removeEventListener('mousedown', handleMouseDown);
document.removeEventListener('mouseup', handleMouseUp);
};
}, [$elementRef, isListening, onOutsideClick]);
};
export default useOnOutsideClick;

View File

@@ -0,0 +1,12 @@
import moment from 'moment';
export const formatDate = (date, format = 'll') => (date ? moment(date).format(format) : date);
export const formatDateTime = (date, format = 'lll') => (date ? moment(date).format(format) : date);
export const formatDateTimeForAPI = date =>
date
? moment(date)
.utc()
.format()
: date;

View File

@@ -0,0 +1,168 @@
import Color from 'color';
export const color = {
primary: '#2553B3', // blue
success: '#29A638', // green
danger: '#E13C3C', // red
warning: '#F89C1C', // orange
accent: '#8A46D7', // purple
textDarkest: '#323232',
textDark: '#616161',
textMedium: '#75787D',
textMediumBlue: '#78869F',
textLight: '#959595',
textLightBlue: '#96A1B5',
backgroundDark: '#8393AD',
backgroundMedium: '#D8DDE6',
backgroundLight: '#F7F9FB',
borderLightest: '#E1E6F0',
borderLight: '#D8DDE6',
borderMedium: '#B9BDC4',
borderBlue: '#C5D3EB',
};
export const sizes = {
appNavBarLeftWidth: 75,
minViewportWidth: 1000,
secondarySideBarWidth: 230,
};
export const zIndexValues = {
modal: 1000,
dropdown: 101,
navLeft: 100,
};
export const font = {
regular: 'font-family: "CircularStdBook"; font-weight: normal;',
medium: 'font-family: "CircularStdMedium"; font-weight: normal;',
bold: 'font-family: "CircularStdBold"; font-weight: normal;',
black: 'font-family: "CircularStdBlack"; font-weight: normal;',
size: size => `font-size: ${size}px;`,
};
export const mixin = {
darken: (colorValue, amount) =>
Color(colorValue)
.darken(amount)
.string(),
lighten: (colorValue, amount) =>
Color(colorValue)
.lighten(amount)
.string(),
rgba: (colorValue, opacity) =>
Color(colorValue)
.alpha(opacity)
.string(),
boxShadowMedium: `
box-shadow: 0 5px 10px 0 rgba(0,0,0,0.1);
`,
boxShadowBorderMedium: `
box-shadow: 0 5px 10px 0 rgba(0,0,0,0.1);
border: 1px solid ${color.borderLight};
border-top: 1px solid ${color.borderLightest};
`,
truncateText: `
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
`,
clickable: `
cursor: pointer;
user-select: none;
`,
hardwareAccelerate: `
transform: translateZ(0);
`,
clearfix: `
*zoom: 1;
&:before,
&:after {
content: " ";
display: table;
}
&:after {
clear: both;
}
`,
cover: `
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
`,
placeholderColor: colorValue => `
::-webkit-input-placeholder {
color: ${colorValue} !important;
opacity: 1 !important;
}
:-moz-placeholder {
color: ${colorValue} !important;
opacity: 1 !important;
}
::-moz-placeholder {
color: ${colorValue} !important;
opacity: 1 !important;
}
:-ms-input-placeholder {
color: ${colorValue} !important;
opacity: 1 !important;
}
`,
scrollableY: `
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
`,
customScrollbar: ({ width = 8, background = mixin.darken(color.backgroundMedium, 0.2) } = {}) => `
&::-webkit-scrollbar {
width: ${width}px;
}
&::-webkit-scrollbar-track {
background: none;
}
&::-webkit-scrollbar-thumb {
border-radius: 99px;
background: ${background};
}
`,
backgroundImage: `
background-position: 50% 50%;
background-repeat: no-repeat;
background-size: cover;
background-color: ${color.backgroundLight};
`,
link: (colorValue = color.primary) => `
cursor: pointer;
color: ${colorValue};
${font.medium}
&:hover, &:visited, &:active {
color: ${colorValue};
}
&:hover {
text-decoration: underline;
}
`,
tag: `
display: inline-block;
height: 24px;
line-height: 22px;
padding: 0 6px 0 8px;
border: 1px solid ${color.borderLight};
border-radius: 4px;
cursor: pointer;
user-select: none;
background: ${color.backgroundLight};
${font.medium}
${font.size(12)}
i {
margin-left: 4px;
vertical-align: middle;
font-size: 14px;
}
`,
};

View File

@@ -0,0 +1,21 @@
import pubsub from 'sweet-pubsub';
import { get } from 'lodash';
const show = toast => pubsub.emit('toast', toast);
const success = title => show({ title });
const error = err => {
show({
type: 'danger',
title: 'Error',
message: get(err, 'message', err),
duration: 0,
});
};
export default {
show,
error,
success,
};

View File

@@ -0,0 +1,13 @@
import queryString from 'query-string';
export const queryStringToObject = (str, options = {}) =>
queryString.parse(str, {
arrayFormat: 'bracket',
...options,
});
export const objectToQueryString = (obj, options = {}) =>
queryString.stringify(obj, {
arrayFormat: 'bracket',
...options,
});

View File

@@ -0,0 +1,36 @@
export const is = {
match: (testFn, message = '') => (value, fieldValues) => !testFn(value, fieldValues) && message,
required: () => value => isNilOrEmptyString(value) && 'This field is required',
minLength: min => value => !!value && value.length < min && `Must be at least ${min} characters`,
maxLength: max => value => !!value && value.length > max && `Must be at most ${max} characters`,
notEmptyArray: () => value =>
Array.isArray(value) && value.length === 0 && 'Please add at least one item',
email: () => value => !!value && !/.+@.+\..+/.test(value) && 'Must be a valid email',
url: () => value =>
!!value &&
// eslint-disable-next-line no-useless-escape
!/^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/.test(value) &&
'Must be a valid URL',
};
const isNilOrEmptyString = value => value === undefined || value === null || value === '';
export const generateErrors = (fieldValues, fieldValidators) => {
const errors = {};
Object.entries(fieldValidators).forEach(([fieldName, validators]) => {
[validators].flat().forEach(validator => {
const errorMessage = validator(fieldValues[fieldName], fieldValues);
if (errorMessage && !errors[fieldName]) {
errors[fieldName] = errorMessage;
}
});
});
return errors;
};

View File

@@ -29,6 +29,7 @@ module.exports = {
], ],
}, },
resolve: { resolve: {
// allows us to do absolute imports from "src"
modules: [path.join(__dirname, 'src'), 'node_modules'], modules: [path.join(__dirname, 'src'), 'node_modules'],
extensions: ['*', '.js', '.jsx'], extensions: ['*', '.js', '.jsx'],
}, },