<commit_before>import React from "react";
import { filter, map, replace, split, find, remove } from "lodash-es";
import { connect } from "react-redux";
import {
    Modal,
    Row,
    Col,
    FormGroup,
    ControlLabel,
    InputGroup
} from "react-bootstrap";
import { push } from "react-router-redux";

import ReadSelector from "./ReadSelector";
import { findReadyHosts, createSample } from "../../actions";
import { Button, Icon, Input, LoadingPlaceholder } from "../../../base";
import { findFiles } from "../../../files/actions";
import { routerLocationHasState } from "../../../utils";

const getReadyHosts = (props) => (
    props.readyHosts && props.readyHosts.length ? (props.readyHosts[0].id || "") : ""
);

const getInitialState = (props) => ({
    selected: [],
    name: "",
    host: "",
    isolate: "",
    locale: "",
    subtraction: getReadyHosts(props),
    group: props.forceGroupChoice ? "none" : "",
    errors: []
});

const SampleUserGroup = ({ group, groups, onChange }) => {
    const groupComponents = map(groups, groupId =>
        <option key={groupId} value={groupId} className="text-capitalize">
            {groupId}
        </option>
    );

    return (
        <Col md={3}>
            <Input type="select" label="User Group" value={group} onChange={onChange}>
                <option key="none" value="none">None</option>
                {groupComponents}
            </Input>
        </Col>
    );
};

class CreateSample extends React.Component {

    constructor (props) {
        super(props);
        this.state = getInitialState(props);
    }

    componentWillReceiveProps (nextProps) {
        if (nextProps.readyHosts !== this.props.readyHosts) {
            this.setState({subtraction: getReadyHosts(nextProps)});
        }
    }

    modalEnter = () => {
        this.props.onFindHosts();
        this.props.onFindFiles();
    };

    handleSubmit = (e) => {
        e.preventDefault();

        const errors = [];

        if (!this.state.name) {
            errors.push({
                id: 0,
                message: "Required Field"
            });
        }

        if (!this.props.readyHosts || !this.props.readyHosts.length) {
            errors.push({
                id: 1,
                message: "A host genome must be added to Virtool before samples can be created and analyzed."
            });
        }

        if (!this.state.selected.length) {
            errors.push({
                id: 2,
                message: "At least one read file must be attached to the sample"
            });
        }

        if (errors.length) {
            this.setState({errors});
            return;
        }

        this.props.onCreate({...this.state, files: this.state.selected});
    };

    autofill = () => {
        this.setState({
            name: split(replace(this.state.selected[0], /[0-9a-z]{8}-/, ""), /_S\d+/)[0]
        });
    };

    handleSelect = (selected) => {
        const newErrors = this.state.errors;

        remove(newErrors, {id: 2});

        this.setState({selected, errors: newErrors});
    };

    render () {

        if (this.props.readyHosts === null || this.props.readFiles) {
            return (
                <Modal show={this.props.show} onHide={this.props.onHide} onEnter={this.modalEnter}>
                    <Modal.Body>
                        <LoadingPlaceholder margin="36px" />
                    </Modal.Body>
                </Modal>
            );
        }

        const hostComponents = map(this.props.readyHosts, host =>
            <option key={host.id}>{host.id}</option>
        );

        const userGroup = this.props.forceGroupChoice ? (
            <SampleUserGroup
                group={this.props.group}
                groups={this.props.groups}
                onChange={(e) => this.setState({group: e})}
            />
        ) : null;

        const libraryType = this.state.selected.length === 2 ? "Paired" : "Unpaired";

        const errorName = find(this.state.errors, ["id", 0]) ? find(this.state.errors, ["id", 0]).message : null;
        const errorHost = find(this.state.errors, ["id", 1]) ? find(this.state.errors, ["id", 1]).message : null;
        const errorFile = find(this.state.errors, ["id", 2]) ? find(this.state.errors, ["id", 2]).message : null;

        return (
            <Modal bsSize="large" show={this.props.show} onHide={this.props.onHide} onEnter={this.modalEnter}>
                <Modal.Header onHide={this.props.onHide} closeButton>
                    Create Sample
                </Modal.Header>

                <form onSubmit={this.handleSubmit}>
                    <Modal.Body>

                        <Row>
                            <Col md={9}>
                                <FormGroup>
                                    <ControlLabel>Sample Name</ControlLabel>
                                    <InputGroup>
                                        <Input
                                            name="sample name"
                                            value={this.state.name}
                                            onChange={(e) => this.setState({name: e.target.value, errors: []})}
                                            autocomplete={false}
                                            error={errorName}
                                        />
                                        <InputGroup.Button style={{verticalAlign: "top", zIndex: "0"}}>
                                            <Button
                                                type="button"
                                                onClick={this.autofill}
                                                disabled={!this.state.selected.length}
                                            >
                                                <Icon name="wand" />
                                            </Button>
                                        </InputGroup.Button>
                                    </InputGroup>
                                </FormGroup>
                            </Col>
                            <Col md={3}>
                                <Input
                                    label="Isolate"
                                    value={this.state.isolate}
                                    onChange={(e) => this.setState({isolate: e.target.value})}
                                />
                            </Col>
                        </Row>

                        <Row>
                            <Col md={6}>
                                <Input
                                    label="True Host"
                                    value={this.state.host}
                                    onChange={(e) => this.setState({host: e.target.value})}
                                />
                            </Col>
                            <Col md={6}>
                                <Input
                                    type="select"
                                    label="Subtraction Host"
                                    value={this.state.subtraction}
                                    onChange={(e) => this.setState({subtraction: e.target.value, errors: []})}
                                    error={errorHost}
                                >
                                    {hostComponents}
                                </Input>
                            </Col>
                        </Row>

                        <Row>
                            <Col md={this.state.forceGroupChoice ? 6 : 8}>
                                <Input
                                    label="Locale"
                                    value={this.state.locale}
                                    onChange={(e) => this.setState({locale: e.target.value})}
                                />
                            </Col>
                            {userGroup}
                            <Col md={this.state.forceGroupChoice ? 3 : 4}>
                                <Input
                                    type="text"
                                    label="Library Type"
                                    value={libraryType}
                                    readOnly={true}
                                />
                            </Col>
                        </Row>

                        <ReadSelector
                            files={this.props.readyReads}
                            selected={this.state.selected}
                            onSelect={this.handleSelect}
                            error={errorFile}
                        />

                    </Modal.Body>

                    <Modal.Footer>
                        <Button type="submit" bsStyle="primary">
                            <Icon name="floppy" /> Save
                        </Button>
                    </Modal.Footer>
                </form>
            </Modal>
        );
    }
}

const mapStateToProps = (state) => {
    const show = routerLocationHasState(state, "create", true);

    return {
        show,
        groups: state.account.groups,
        readyHosts: state.samples.readyHosts,
        readyReads: filter(state.files.documents, {type: "reads", reserved: false}),
        forceGroupChoice: state.settings.sample_group === "force_choice"
    };
};

const mapDispatchToProps = (dispatch) => ({

    onFindHosts: () => {
        dispatch(findReadyHosts());
    },

    onFindFiles: () => {
        dispatch(findFiles("reads", 1));
    },

    onCreate: ({ name, isolate, host, locale, subtraction, files }) => {
        dispatch(createSample(name, isolate, host, locale, subtraction, files));
    },

    onHide: () => {
        dispatch(push({...window.location, state: {create: false}}));
    }

});

export default connect(mapStateToProps, mapDispatchToProps)(CreateSample);
<commit_msg>Edit clear fields on modal exit<commit_after>import React from "react";
import { filter, map, replace, split, find, remove } from "lodash-es";
import { connect } from "react-redux";
import {
    Modal,
    Row,
    Col,
    FormGroup,
    ControlLabel,
    InputGroup
} from "react-bootstrap";
import { push } from "react-router-redux";

import ReadSelector from "./ReadSelector";
import { findReadyHosts, createSample } from "../../actions";
import { Button, Icon, Input, LoadingPlaceholder } from "../../../base";
import { findFiles } from "../../../files/actions";
import { routerLocationHasState } from "../../../utils";

const getReadyHosts = (props) => (
    props.readyHosts && props.readyHosts.length ? (props.readyHosts[0].id || "") : ""
);

const getInitialState = (props) => ({
    selected: [],
    name: "",
    host: "",
    isolate: "",
    locale: "",
    subtraction: getReadyHosts(props),
    group: props.forceGroupChoice ? "none" : "",
    errors: []
});

const SampleUserGroup = ({ group, groups, onChange }) => {
    const groupComponents = map(groups, groupId =>
        <option key={groupId} value={groupId} className="text-capitalize">
            {groupId}
        </option>
    );

    return (
        <Col md={3}>
            <Input type="select" label="User Group" value={group} onChange={onChange}>
                <option key="none" value="none">None</option>
                {groupComponents}
            </Input>
        </Col>
    );
};

class CreateSample extends React.Component {

    constructor (props) {
        super(props);
        this.state = getInitialState(props);
    }

    componentWillReceiveProps (nextProps) {
        if (nextProps.readyHosts !== this.props.readyHosts) {
            this.setState({subtraction: getReadyHosts(nextProps)});
        }
    }

    modalEnter = () => {
        this.props.onFindHosts();
        this.props.onFindFiles();
    };

    handleModalExited = () => {
        this.setState({
            selected: [],
            name: "",
            host: "",
            isolate: "",
            locale: "",
            group: this.props.forceGroupChoice ? "none" : "",
            errors: []
        });
    };

    handleSubmit = (e) => {
        e.preventDefault();

        const errors = [];

        if (!this.state.name) {
            errors.push({
                id: 0,
                message: "Required Field"
            });
        }

        if (!this.props.readyHosts || !this.props.readyHosts.length) {
            errors.push({
                id: 1,
                message: "A host genome must be added to Virtool before samples can be created and analyzed."
            });
        }

        if (!this.state.selected.length) {
            errors.push({
                id: 2,
                message: "At least one read file must be attached to the sample"
            });
        }

        if (errors.length) {
            this.setState({errors});
            return;
        }

        this.props.onCreate({...this.state, files: this.state.selected});
    };

    autofill = () => {
        this.setState({
            name: split(replace(this.state.selected[0], /[0-9a-z]{8}-/, ""), /_S\d+/)[0]
        });
    };

    handleSelect = (selected) => {
        const newErrors = this.state.errors;

        remove(newErrors, {id: 2});

        this.setState({selected, errors: newErrors});
    };

    render () {

        if (this.props.readyHosts === null || this.props.readFiles) {
            return (
                <Modal show={this.props.show} onHide={this.props.onHide} onEnter={this.modalEnter}>
                    <Modal.Body>
                        <LoadingPlaceholder margin="36px" />
                    </Modal.Body>
                </Modal>
            );
        }

        const hostComponents = map(this.props.readyHosts, host =>
            <option key={host.id}>{host.id}</option>
        );

        const userGroup = this.props.forceGroupChoice ? (
            <SampleUserGroup
                group={this.props.group}
                groups={this.props.groups}
                onChange={(e) => this.setState({group: e})}
            />
        ) : null;

        const libraryType = this.state.selected.length === 2 ? "Paired" : "Unpaired";

        const errorName = find(this.state.errors, ["id", 0]) ? find(this.state.errors, ["id", 0]).message : null;
        const errorHost = find(this.state.errors, ["id", 1]) ? find(this.state.errors, ["id", 1]).message : null;
        const errorFile = find(this.state.errors, ["id", 2]) ? find(this.state.errors, ["id", 2]).message : null;

        return (
            <Modal bsSize="large" show={this.props.show} onHide={this.props.onHide} onEnter={this.modalEnter} onExited={this.handleModalExited}>
                <Modal.Header onHide={this.props.onHide} closeButton>
                    Create Sample
                </Modal.Header>

                <form onSubmit={this.handleSubmit}>
                    <Modal.Body>

                        <Row>
                            <Col md={9}>
                                <FormGroup>
                                    <ControlLabel>Sample Name</ControlLabel>
                                    <InputGroup>
                                        <Input
                                            name="sample name"
                                            value={this.state.name}
                                            onChange={(e) => this.setState({name: e.target.value, errors: []})}
                                            autocomplete={false}
                                            error={errorName}
                                        />
                                        <InputGroup.Button style={{verticalAlign: "top", zIndex: "0"}}>
                                            <Button
                                                type="button"
                                                onClick={this.autofill}
                                                disabled={!this.state.selected.length}
                                            >
                                                <Icon name="wand" />
                                            </Button>
                                        </InputGroup.Button>
                                    </InputGroup>
                                </FormGroup>
                            </Col>
                            <Col md={3}>
                                <Input
                                    label="Isolate"
                                    value={this.state.isolate}
                                    onChange={(e) => this.setState({isolate: e.target.value})}
                                />
                            </Col>
                        </Row>

                        <Row>
                            <Col md={6}>
                                <Input
                                    label="True Host"
                                    value={this.state.host}
                                    onChange={(e) => this.setState({host: e.target.value})}
                                />
                            </Col>
                            <Col md={6}>
                                <Input
                                    type="select"
                                    label="Subtraction Host"
                                    value={this.state.subtraction}
                                    onChange={(e) => this.setState({subtraction: e.target.value, errors: []})}
                                    error={errorHost}
                                >
                                    {hostComponents}
                                </Input>
                            </Col>
                        </Row>

                        <Row>
                            <Col md={this.state.forceGroupChoice ? 6 : 8}>
                                <Input
                                    label="Locale"
                                    value={this.state.locale}
                                    onChange={(e) => this.setState({locale: e.target.value})}
                                />
                            </Col>
                            {userGroup}
                            <Col md={this.state.forceGroupChoice ? 3 : 4}>
                                <Input
                                    type="text"
                                    label="Library Type"
                                    value={libraryType}
                                    readOnly={true}
                                />
                            </Col>
                        </Row>

                        <ReadSelector
                            files={this.props.readyReads}
                            selected={this.state.selected}
                            onSelect={this.handleSelect}
                            error={errorFile}
                        />

                    </Modal.Body>

                    <Modal.Footer>
                        <Button type="submit" bsStyle="primary">
                            <Icon name="floppy" /> Save
                        </Button>
                    </Modal.Footer>
                </form>
            </Modal>
        );
    }
}

const mapStateToProps = (state) => {
    const show = routerLocationHasState(state, "create", true);

    return {
        show,
        groups: state.account.groups,
        readyHosts: state.samples.readyHosts,
        readyReads: filter(state.files.documents, {type: "reads", reserved: false}),
        forceGroupChoice: state.settings.sample_group === "force_choice"
    };
};

const mapDispatchToProps = (dispatch) => ({

    onFindHosts: () => {
        dispatch(findReadyHosts());
    },

    onFindFiles: () => {
        dispatch(findFiles("reads", 1));
    },

    onCreate: ({ name, isolate, host, locale, subtraction, files }) => {
        dispatch(createSample(name, isolate, host, locale, subtraction, files));
    },

    onHide: () => {
        dispatch(push({...window.location, state: {create: false}}));
    }

});

export default connect(mapStateToProps, mapDispatchToProps)(CreateSample);

{'fraction_non_alphanumeric': 0.12382133995037221, 'fraction_numerical': 0.002977667493796526, 'mean_word_length': 1.0714432565789473, 'pattern_counts': {'":': 0, '<': 135, '<?xml version=': 0, '>': 178, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 100, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '408406', 'n_tokens_mistral': 4928, 'n_tokens_neox': 4687, 'n_words': 903}