From 679d60cd5a5debcacff42c38967c1f8b7d972882 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 11 Jun 2018 12:38:25 -0700 Subject: Implement large screen open positions --- .../ts/components/relayer_index/relayer_index.tsx | 32 +--- packages/website/ts/components/ui/retry.tsx | 33 ++++ packages/website/ts/pages/jobs/benefits.tsx | 2 +- packages/website/ts/pages/jobs/mission.tsx | 2 +- packages/website/ts/pages/jobs/open_positions.tsx | 196 +++++++++++++-------- packages/website/ts/pages/jobs/teams.tsx | 2 +- packages/website/ts/style/colors.ts | 3 +- packages/website/ts/types.ts | 8 + packages/website/ts/utils/backend_client.ts | 13 +- 9 files changed, 187 insertions(+), 104 deletions(-) create mode 100644 packages/website/ts/components/ui/retry.tsx (limited to 'packages/website/ts') diff --git a/packages/website/ts/components/relayer_index/relayer_index.tsx b/packages/website/ts/components/relayer_index/relayer_index.tsx index 683f7084b..69a7cada1 100644 --- a/packages/website/ts/components/relayer_index/relayer_index.tsx +++ b/packages/website/ts/components/relayer_index/relayer_index.tsx @@ -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 {_.isUndefined(this.state.error) ? ( @@ -124,31 +126,3 @@ export class RelayerIndex extends React.Component void; -} -const Retry = (props: RetryProps) => ( -
-
-
- Something went wrong. -
-
- -
-
-
-); diff --git a/packages/website/ts/components/ui/retry.tsx b/packages/website/ts/components/ui/retry.tsx new file mode 100644 index 000000000..f18b5abac --- /dev/null +++ b/packages/website/ts/components/ui/retry.tsx @@ -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) => ( +
+
+
+ Something went wrong. +
+
+ +
+
+
+); diff --git a/packages/website/ts/pages/jobs/benefits.tsx b/packages/website/ts/pages/jobs/benefits.tsx index ce261592f..a7cc23503 100644 --- a/packages/website/ts/pages/jobs/benefits.tsx +++ b/packages/website/ts/pages/jobs/benefits.tsx @@ -41,7 +41,7 @@ export interface BenefitsProps { } export const Benefits = (props: BenefitsProps) => ( -
+
{props.screenWidth === ScreenWidths.Sm ? : }
); diff --git a/packages/website/ts/pages/jobs/mission.tsx b/packages/website/ts/pages/jobs/mission.tsx index a3584e5f6..b4d294623 100644 --- a/packages/website/ts/pages/jobs/mission.tsx +++ b/packages/website/ts/pages/jobs/mission.tsx @@ -35,7 +35,7 @@ export const Mission = (props: MissionProps) => {
); return ( -
+
{isSmallScreen ? (
diff --git a/packages/website/ts/pages/jobs/open_positions.tsx b/packages/website/ts/pages/jobs/open_positions.tsx index f9c37d36f..5eb8e429d 100644 --- a/packages/website/ts/pages/jobs/open_positions.tsx +++ b/packages/website/ts/pages/jobs/open_positions.tsx @@ -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 ( -
- - - - - Position - - - Department - - - Office - - - - - {_.map(POSITIONS, position => { - return ( - - - {position.name} - - - {position.department} - - - {position.office} - +export class OpenPositions extends React.Component { + 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 +
+ {_.isUndefined(this.state.error) ? ( + + ) : ( + + )} +
+ ); + } else { + return ( +
+ + <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> +); diff --git a/packages/website/ts/pages/jobs/teams.tsx b/packages/website/ts/pages/jobs/teams.tsx index 3d953c993..465bae7f4 100644 --- a/packages/website/ts/pages/jobs/teams.tsx +++ b/packages/website/ts/pages/jobs/teams.tsx @@ -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> diff --git a/packages/website/ts/style/colors.ts b/packages/website/ts/style/colors.ts index 002318e14..539f3e125 100644 --- a/packages/website/ts/style/colors.ts +++ b/packages/website/ts/style/colors.ts @@ -11,7 +11,8 @@ const appColors = { wrapEtherConfirmationButton: sharedColors.mediumBlue, drawerMenuBackground: '#4a4a4a', menuItemDefaultSelectedBackground: '#424242', - jobsPageGrey: '#fafafa', + jobsPageBackground: '#fafafa', + jobsPageOpenPositionRow: '#f5f5f5', }; export const colors = { diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 15444e517..24e86a901 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -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 diff --git a/packages/website/ts/utils/backend_client.ts b/packages/website/ts/utils/backend_client.ts index 6b16aea6b..835a6ef4d 100644 --- a/packages/website/ts/utils/backend_client.ts +++ b/packages/website/ts/utils/backend_client.ts @@ -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 {}; -- cgit