Implement large screen open positions
This commit is contained in:
@@ -6,6 +6,7 @@ import { GridList } from 'material-ui/GridList';
|
||||
import * as React from 'react';
|
||||
|
||||
import { RelayerGridTile } from 'ts/components/relayer_index/relayer_grid_tile';
|
||||
import { Retry } from 'ts/components/ui/retry';
|
||||
import { colors } from 'ts/style/colors';
|
||||
import { ScreenWidths, WebsiteBackendRelayerInfo } from 'ts/types';
|
||||
import { backendClient } from 'ts/utils/backend_client';
|
||||
@@ -63,7 +64,8 @@ export class RelayerIndex extends React.Component<RelayerIndexProps, RelayerInde
|
||||
const isReadyToRender = _.isUndefined(this.state.error) && !_.isUndefined(this.state.relayerInfos);
|
||||
if (!isReadyToRender) {
|
||||
return (
|
||||
// TODO: consolidate this loading component with the one in portal
|
||||
// TODO: consolidate this loading component with the one in portal and OpenPositions
|
||||
// TODO: possibly refactor into a generic loading container with spinner and retry UI
|
||||
<div className="center">
|
||||
{_.isUndefined(this.state.error) ? (
|
||||
<CircularProgress size={40} thickness={5} />
|
||||
@@ -124,31 +126,3 @@ export class RelayerIndex extends React.Component<RelayerIndexProps, RelayerInde
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface RetryProps {
|
||||
onRetry: () => void;
|
||||
}
|
||||
const Retry = (props: RetryProps) => (
|
||||
<div className="clearfix center" style={{ color: colors.black }}>
|
||||
<div className="mx-auto inline-block align-middle" style={{ lineHeight: '44px', textAlign: 'center' }}>
|
||||
<div className="h2" style={{ fontFamily: 'Roboto Mono' }}>
|
||||
Something went wrong.
|
||||
</div>
|
||||
<div className="py3">
|
||||
<FlatButton
|
||||
label={'reload'}
|
||||
backgroundColor={colors.black}
|
||||
labelStyle={{
|
||||
fontSize: 18,
|
||||
fontFamily: 'Roboto Mono',
|
||||
fontWeight: 'lighter',
|
||||
color: colors.white,
|
||||
textTransform: 'lowercase',
|
||||
}}
|
||||
style={{ width: 280, height: 62, borderRadius: 5 }}
|
||||
onClick={props.onRetry}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
33
packages/website/ts/components/ui/retry.tsx
Normal file
33
packages/website/ts/components/ui/retry.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import FlatButton from 'material-ui/FlatButton';
|
||||
import { GridList } from 'material-ui/GridList';
|
||||
import * as React from 'react';
|
||||
|
||||
import { colors } from 'ts/style/colors';
|
||||
|
||||
export interface RetryProps {
|
||||
onRetry: () => void;
|
||||
}
|
||||
export const Retry = (props: RetryProps) => (
|
||||
<div className="clearfix center" style={{ color: colors.black }}>
|
||||
<div className="mx-auto inline-block align-middle" style={{ lineHeight: '44px', textAlign: 'center' }}>
|
||||
<div className="h2" style={{ fontFamily: 'Roboto Mono' }}>
|
||||
Something went wrong.
|
||||
</div>
|
||||
<div className="py3">
|
||||
<FlatButton
|
||||
label={'reload'}
|
||||
backgroundColor={colors.black}
|
||||
labelStyle={{
|
||||
fontSize: 18,
|
||||
fontFamily: 'Roboto Mono',
|
||||
fontWeight: 'lighter',
|
||||
color: colors.white,
|
||||
textTransform: 'lowercase',
|
||||
}}
|
||||
style={{ width: 280, height: 62, borderRadius: 5 }}
|
||||
onClick={props.onRetry}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -41,7 +41,7 @@ export interface BenefitsProps {
|
||||
}
|
||||
|
||||
export const Benefits = (props: BenefitsProps) => (
|
||||
<div style={{ backgroundColor: colors.jobsPageGrey }}>
|
||||
<div style={{ backgroundColor: colors.jobsPageBackground }}>
|
||||
{props.screenWidth === ScreenWidths.Sm ? <SmallLayout /> : <LargeLayout />}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -35,7 +35,7 @@ export const Mission = (props: MissionProps) => {
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div className="container lg-py4 md-py4" style={{ backgroundColor: colors.jobsPageGrey, color: colors.black }}>
|
||||
<div className="container lg-py4 md-py4" style={{ backgroundColor: colors.jobsPageBackground, color: colors.black }}>
|
||||
<div className="mx-auto clearfix sm-py4">
|
||||
{isSmallScreen ? (
|
||||
<div>
|
||||
|
||||
@@ -1,80 +1,136 @@
|
||||
import * as _ from 'lodash';
|
||||
import CircularProgress from 'material-ui/CircularProgress';
|
||||
import { Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn } from 'material-ui/Table';
|
||||
import * as React from 'react';
|
||||
|
||||
const POSITIONS = [
|
||||
{
|
||||
name: 'Community Director',
|
||||
department: 'Marketing',
|
||||
office: 'Remote / San Francisco',
|
||||
},
|
||||
{
|
||||
name: 'Data Scientist / Data Engineer',
|
||||
department: 'Engineering',
|
||||
office: 'Remote / San Francisco',
|
||||
},
|
||||
{
|
||||
name: 'Executive Assitant / Office Manager',
|
||||
department: 'Operations',
|
||||
office: 'Remote / San Francisco',
|
||||
},
|
||||
{
|
||||
name: 'Research Fellow - Economics / Governance',
|
||||
department: 'Engineering',
|
||||
office: 'Remote / San Francisco',
|
||||
},
|
||||
{
|
||||
name: 'Software Engineer - Blockchain',
|
||||
department: 'Engineer',
|
||||
office: 'Remote / San Francisco',
|
||||
},
|
||||
{
|
||||
name: 'Software Engineer - Full-stack',
|
||||
department: 'Marketing',
|
||||
office: 'Remote / San Francisco',
|
||||
},
|
||||
];
|
||||
import { Retry } from 'ts/components/ui/retry';
|
||||
import { colors } from 'ts/style/colors';
|
||||
import { WebsiteBackendJobInfo } from 'ts/types';
|
||||
import { backendClient } from 'ts/utils/backend_client';
|
||||
|
||||
const labelStyle = { fontFamily: 'Roboto Mono', fontSize: 18 };
|
||||
|
||||
export interface OpenPositionsProps {
|
||||
hash: string;
|
||||
}
|
||||
export interface OpenPositionsState {
|
||||
jobInfos?: WebsiteBackendJobInfo[];
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
export const OpenPositions = (props: OpenPositionsProps) => {
|
||||
const labelStyle = { fontFamily: 'Roboto Mono', fontSize: 18 };
|
||||
return (
|
||||
<div id={props.hash} className="py4" style={{ paddingLeft: 200, paddingRight: 200 }}>
|
||||
<Table selectable={false}>
|
||||
<TableHeader displaySelectAll={false} adjustForCheckbox={false}>
|
||||
<TableRow>
|
||||
<TableHeaderColumn colSpan={6} style={labelStyle}>
|
||||
Position
|
||||
</TableHeaderColumn>
|
||||
<TableHeaderColumn colSpan={3} style={labelStyle}>
|
||||
Department
|
||||
</TableHeaderColumn>
|
||||
<TableHeaderColumn colSpan={3} style={labelStyle}>
|
||||
Office
|
||||
</TableHeaderColumn>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody displayRowCheckbox={false} showRowHover={true}>
|
||||
{_.map(POSITIONS, position => {
|
||||
return (
|
||||
<TableRow hoverable={true} displayBorder={false} style={{ height: 100, border: 2 }}>
|
||||
<TableRowColumn colSpan={6} style={labelStyle}>
|
||||
{position.name}
|
||||
</TableRowColumn>
|
||||
<TableRowColumn colSpan={3} style={labelStyle}>
|
||||
{position.department}
|
||||
</TableRowColumn>
|
||||
<TableRowColumn colSpan={3} style={labelStyle}>
|
||||
{position.office}
|
||||
</TableRowColumn>
|
||||
export class OpenPositions extends React.Component<OpenPositionsProps, OpenPositionsState> {
|
||||
private _isUnmounted: boolean;
|
||||
constructor(props: OpenPositionsProps) {
|
||||
super(props);
|
||||
this._isUnmounted = false;
|
||||
this.state = {
|
||||
jobInfos: undefined,
|
||||
error: undefined,
|
||||
};
|
||||
}
|
||||
public componentWillMount(): void {
|
||||
// tslint:disable-next-line:no-floating-promises
|
||||
this._fetchJobInfosAsync();
|
||||
}
|
||||
public componentWillUnmount(): void {
|
||||
this._isUnmounted = true;
|
||||
}
|
||||
public render(): React.ReactNode {
|
||||
const isReadyToRender = _.isUndefined(this.state.error) && !_.isUndefined(this.state.jobInfos);
|
||||
if (!isReadyToRender) {
|
||||
return (
|
||||
// TODO: consolidate this loading component with the one in portal and RelayerIndex
|
||||
// TODO: possibly refactor into a generic loading container with spinner and retry UI
|
||||
<div className="center">
|
||||
{_.isUndefined(this.state.error) ? (
|
||||
<CircularProgress size={40} thickness={5} />
|
||||
) : (
|
||||
<Retry onRetry={this._fetchJobInfosAsync.bind(this)} />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div id={this.props.hash} className="mx-auto max-width-4">
|
||||
<Title />
|
||||
<Table selectable={false} onCellClick={this._onCellClick.bind(this)}>
|
||||
<TableHeader displaySelectAll={false} adjustForCheckbox={false}>
|
||||
<TableRow>
|
||||
<TableHeaderColumn colSpan={5} style={labelStyle}>
|
||||
Position
|
||||
</TableHeaderColumn>
|
||||
<TableHeaderColumn colSpan={3} style={labelStyle}>
|
||||
Department
|
||||
</TableHeaderColumn>
|
||||
<TableHeaderColumn colSpan={4} style={labelStyle}>
|
||||
Office
|
||||
</TableHeaderColumn>
|
||||
</TableRow>
|
||||
);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
</TableHeader>
|
||||
<TableBody displayRowCheckbox={false} showRowHover={true}>
|
||||
{_.map(this.state.jobInfos, jobInfo => {
|
||||
return this._renderJobInfo(jobInfo);
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
private _renderJobInfo(jobInfo: WebsiteBackendJobInfo): React.ReactNode {
|
||||
return (
|
||||
<TableRow key={jobInfo.id} hoverable={true} displayBorder={false} style={{ height: 100, border: 2 }}>
|
||||
<TableRowColumn colSpan={5} style={labelStyle}>
|
||||
{jobInfo.title}
|
||||
</TableRowColumn>
|
||||
<TableRowColumn colSpan={3} style={labelStyle}>
|
||||
{jobInfo.department}
|
||||
</TableRowColumn>
|
||||
<TableRowColumn colSpan={4} style={labelStyle}>
|
||||
{jobInfo.office}
|
||||
</TableRowColumn>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
private async _fetchJobInfosAsync(): Promise<void> {
|
||||
try {
|
||||
if (!this._isUnmounted) {
|
||||
this.setState({
|
||||
jobInfos: undefined,
|
||||
error: undefined,
|
||||
});
|
||||
}
|
||||
const jobInfos = await backendClient.getJobInfosAsync();
|
||||
if (!this._isUnmounted) {
|
||||
this.setState({
|
||||
jobInfos,
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
if (!this._isUnmounted) {
|
||||
this.setState({
|
||||
error,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
private _onCellClick(rowNumber: number): void {
|
||||
if (_.isUndefined(this.state.jobInfos)) {
|
||||
return;
|
||||
}
|
||||
const url = this.state.jobInfos[rowNumber].url;
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
}
|
||||
|
||||
const Title = () => (
|
||||
<div
|
||||
className="h2 lg-py4 md-py4 sm-py3"
|
||||
style={{
|
||||
paddingLeft: 90,
|
||||
fontFamily: 'Roboto Mono',
|
||||
}}
|
||||
>
|
||||
{'Open Positions'}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -42,7 +42,7 @@ export interface TeamsProps {
|
||||
export const Teams = (props: TeamsProps) => (props.screenWidth === ScreenWidths.Sm ? <SmallLayout /> : <LargeLayout />);
|
||||
|
||||
const LargeLayout = () => (
|
||||
<div className="mx-auto max-width-4 clearfix">
|
||||
<div className="mx-auto max-width-4 clearfix pb4">
|
||||
<div className="col lg-col-6 md-col-6 col-12">
|
||||
<BulletedItemList headerText={HEADER_TEXT} bulletedItemInfos={ITEMS_COLUMN1} />
|
||||
</div>
|
||||
|
||||
@@ -11,7 +11,8 @@ const appColors = {
|
||||
wrapEtherConfirmationButton: sharedColors.mediumBlue,
|
||||
drawerMenuBackground: '#4a4a4a',
|
||||
menuItemDefaultSelectedBackground: '#424242',
|
||||
jobsPageGrey: '#fafafa',
|
||||
jobsPageBackground: '#fafafa',
|
||||
jobsPageOpenPositionRow: '#f5f5f5',
|
||||
};
|
||||
|
||||
export const colors = {
|
||||
|
||||
@@ -536,4 +536,12 @@ export interface WebsiteBackendTokenInfo {
|
||||
export interface WebsiteBackendGasInfo {
|
||||
average: number;
|
||||
}
|
||||
|
||||
export interface WebsiteBackendJobInfo {
|
||||
id: number;
|
||||
title: string;
|
||||
department: string;
|
||||
office: string;
|
||||
url: string;
|
||||
}
|
||||
// tslint:disable:max-file-line-count
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import * as _ from 'lodash';
|
||||
|
||||
import { ArticlesBySection, WebsiteBackendGasInfo, WebsiteBackendPriceInfo, WebsiteBackendRelayerInfo } from 'ts/types';
|
||||
import {
|
||||
ArticlesBySection,
|
||||
WebsiteBackendGasInfo,
|
||||
WebsiteBackendJobInfo,
|
||||
WebsiteBackendPriceInfo,
|
||||
WebsiteBackendRelayerInfo,
|
||||
} from 'ts/types';
|
||||
import { fetchUtils } from 'ts/utils/fetch_utils';
|
||||
import { utils } from 'ts/utils/utils';
|
||||
|
||||
const ETH_GAS_STATION_ENDPOINT = '/eth_gas_station';
|
||||
const JOBS_ENDPOINT = '/jobs';
|
||||
const PRICES_ENDPOINT = '/prices';
|
||||
const RELAYERS_ENDPOINT = '/relayers';
|
||||
const WIKI_ENDPOINT = '/wiki';
|
||||
@@ -15,6 +22,10 @@ export const backendClient = {
|
||||
const result = await fetchUtils.requestAsync(utils.getBackendBaseUrl(), ETH_GAS_STATION_ENDPOINT);
|
||||
return result;
|
||||
},
|
||||
async getJobInfosAsync(): Promise<WebsiteBackendJobInfo[]> {
|
||||
const result = await fetchUtils.requestAsync(utils.getBackendBaseUrl(), JOBS_ENDPOINT);
|
||||
return result;
|
||||
},
|
||||
async getPriceInfoAsync(tokenSymbols: string[]): Promise<WebsiteBackendPriceInfo> {
|
||||
if (_.isEmpty(tokenSymbols)) {
|
||||
return {};
|
||||
|
||||
Reference in New Issue
Block a user