aboutsummaryrefslogblamecommitdiffstats
path: root/packages/website/ts/pages/jobs/open_positions.tsx
blob: 892653afddd7943c366acd81eebd78a1c488284f (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                            
                                                            

                               
                                                       
                                               
                                             
                                         

                                                               
                                                        
                                       
 
                                 
 

                                     
                              
 



                                       
 


















                                                                                                       
                                                                          
                
                                                                                          
                                    
                                                                                                                    
                  






                                                                                            

          











                                                                                                 
     

                                             











                                                                              

          










                                                                    
                             









                                     

                                                                   
                           


     



                                       


                                                             
































                                                                                                         
 
                                                            
                    

                                        



                     
import * as _ from 'lodash';
import CircularProgress from 'material-ui/CircularProgress';
import * as React from 'react';

import { Container } from 'ts/components/ui/container';
import { Retry } from 'ts/components/ui/retry';
import { Text } from 'ts/components/ui/text';
import { colors } from 'ts/style/colors';
import { styled } from 'ts/style/theme';
import { ScreenWidths, WebsiteBackendJobInfo } from 'ts/types';
import { backendClient } from 'ts/utils/backend_client';
import { utils } from 'ts/utils/utils';

const TABLE_ROW_MIN_HEIGHT = 100;

export interface OpenPositionsProps {
    hash: string;
    screenWidth: ScreenWidths;
}
export interface OpenPositionsState {
    jobInfos?: WebsiteBackendJobInfo[];
    error?: Error;
}

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);
        const isSmallScreen = utils.isMobileWidth(this.props.screenWidth);
        return (
            <Container id={this.props.hash} className="mx-auto pb4 px3" maxWidth="1200px">
                {!isSmallScreen && (
                    <hr style={{ border: 0, borderTop: 1, borderStyle: 'solid', borderColor: colors.beigeWhite }} />
                )}
                <Container marginTop="64px" marginBottom="50px">
                    <Text fontFamily="Roboto Mono" fontSize="24px" fontColor={colors.black}>
                        Open Positions
                    </Text>
                </Container>
                {isReadyToRender ? this._renderTable() : this._renderLoading()}
            </Container>
        );
    }
    private _renderLoading(): React.ReactNode {
        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>
        );
    }
    private _renderTable(): React.ReactNode {
        return (
            <Container width="100%">
                {_.map(this.state.jobInfos, jobInfo => {
                    return (
                        <JobInfoTableRow
                            key={jobInfo.id}
                            screenWidth={this.props.screenWidth}
                            jobInfo={jobInfo}
                            onClick={this._openJobInfoUrl.bind(this, jobInfo)}
                        />
                    );
                })}
            </Container>
        );
    }
    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 _openJobInfoUrl(jobInfo: WebsiteBackendJobInfo): void {
        const url = jobInfo.url;
        utils.openUrl(url);
    }
}

export interface JobInfoTableRowProps {
    className?: string;
    screenWidth: ScreenWidths;
    jobInfo: WebsiteBackendJobInfo;
    onClick?: (event: React.MouseEvent<HTMLElement>) => void;
}

const PlainJobInfoTableRow: React.StatelessComponent<JobInfoTableRowProps> = ({
    className,
    screenWidth,
    jobInfo,
    onClick,
}) => {
    const isSmallScreen = screenWidth === ScreenWidths.Sm;
    const titleClassName = isSmallScreen ? 'col col-12 center' : 'col col-5';
    const paddingLeft = isSmallScreen ? undefined : '30px';
    return (
        <Container className={className} onClick={onClick} marginBottom="30px" paddingLeft={paddingLeft}>
            <Container className="flex items-center" minHeight={TABLE_ROW_MIN_HEIGHT} width="100%">
                <Container className="clearfix container" width="100%">
                    <Container className={titleClassName}>
                        <Text fontSize="16px" fontWeight="bold" fontColor={colors.mediumBlue}>
                            {jobInfo.title}
                        </Text>
                    </Container>
                    {!isSmallScreen && (
                        <Container className="col col-3">
                            <Text fontSize="16px">{jobInfo.department}</Text>
                        </Container>
                    )}
                    {!isSmallScreen && (
                        <Container className="col col-4 center">
                            <Text fontSize="16px">{jobInfo.office}</Text>
                        </Container>
                    )}
                </Container>
            </Container>
        </Container>
    );
};

export const JobInfoTableRow = styled(PlainJobInfoTableRow)`
    cursor: pointer;
    background-color: ${colors.grey100};
    border-radius: 7px;
    &:hover {
        opacity: 0.5;
    }
`;