<commit_before>import React, { Component, PropTypes } from "react";

import FormField from "metabase/components/FormField.jsx";
import ModalContent from "metabase/components/ModalContent.jsx";

import Query from "metabase/lib/query";
import { cancelable } from "metabase/lib/promise";

import cx from "classnames";


export default class SaveQuestionModal extends Component {

    constructor(props, context) {
        super(props, context);

        const isStructured = Query.isStructured(props.card.dataset_query);

        this.state = {
            error: null,
            valid: false,
            details: {
                name: props.card.name || isStructured ? Query.generateQueryDescription(props.tableMetadata, props.card.dataset_query.query) : "",
                description: props.card.description || null,
                saveType: "create"
            }
        };
    }

    static propTypes = {
        card: PropTypes.object.isRequired,
        originalCard: PropTypes.object,
        tableMetadata: PropTypes.object, // can't be required, sometimes null
        createFn: PropTypes.func.isRequired,
        saveFn: PropTypes.func.isRequired,
        closeFn: PropTypes.func.isRequired
    }

    componentDidMount() {
        this.validateForm();
    }

    componentDidUpdate() {
        this.validateForm();
    }

    componentWillUnmount() {
        if (this.requestPromise) {
            this.requestPromise.cancel();
        }
    }

    validateForm() {
        let { details } = this.state;

        let valid = true;

        // name is required
        if (!details.name) {
            valid = false;
        }

        if (this.state.valid !== valid) {
            this.setState({ valid });
        }
    }

    onChange(fieldName, fieldValue) {
        if (fieldName === "saveType" && fieldValue === "overwrite" && this.state.details.saveType !== "overwrite") {
            // when someone chooses overwrite we want to populate the name/description from the original card
            this.setState({
                details: {
                    name: this.props.originalCard.name,
                    description: this.props.originalCard.description,
                    saveType: "overwrite"
                }
            });
        } else {
            this.setState({ details: { ...this.state.details, [fieldName]: fieldValue ? fieldValue : null }});
        }
    }

    async formSubmitted(e) {
        try {
            e.preventDefault();

            let { details } = this.state;
            let { card, addToDashboard, createFn, saveFn } = this.props;

            card = {
                ...card,
                name: details.name.trim(),
                // since description is optional, it can be null, so check for a description before trimming it
                description: details.description ? details.description.trim() : null,
                public_perms: 2
            };

            if (details.saveType === "create") {
                this.requestPromise = cancelable(createFn(card, addToDashboard));
            } else if (details.saveType === "overwrite") {
                card.id = this.props.originalCard.id;
                this.requestPromise = cancelable(saveFn(card, addToDashboard));
            }

            await this.requestPromise;
            this.props.closeFn();
        } catch (error) {
            if (error && !error.isCanceled) {
                this.setState({ error: error });
            }
        }
    }

    render() {
        let { error } = this.state;
        var formError;
        if (error) {
            var errorMessage;
            if (error.status === 500) {
                errorMessage = "Server error encountered";
            }

            if (error.data && error.data.message) {
                errorMessage = error.data.message;
            }
            if (error.data && error.data.errors) {
                errorMessage = Object.values(error.data.errors);
            }

            // TODO: timeout display?
            if (errorMessage) {
                formError = (
                    <span className="text-error px2">{errorMessage}</span>
                );
            }
        }

        var saveOrUpdate = null;
        if (!this.props.card.id && this.props.originalCard) {
            saveOrUpdate = (
                <FormField
                    displayName="Save or Replace?"
                    fieldName="saveType"
                    errors={this.state.errors}>
                    <ul>
                        <li onClick={(e) => this.onChange("saveType", "create")}><input type="radio" name="saveType" value="create" checked={this.state.details.saveType === "create"} /> Save as a new question?</li>
                        <li onClick={(e) => this.onChange("saveType", "overwrite")}><input type="radio" name="saveType" value="overwrite" checked={this.state.details.saveType === "overwrite"} /> Replace original question, "{this.props.originalCard.name}"</li>
                    </ul>
                </FormField>
            );
        }

        let title = this.props.addToDashboard ? "First, Save Your Question" : "Save Question";

        return (
            <ModalContent
                title={title}
                closeFn={this.props.closeFn}
            >
                <form className="flex flex-column flex-full" onSubmit={(e) => this.formSubmitted(e)}>
                    <div className="Form-inputs">
                        {saveOrUpdate}

                        <FormField
                            displayName="Name"
                            fieldName="name"
                            errors={this.state.errors}>
                            <input className="Form-input full" name="name" placeholder="What is the name of your card?" value={this.state.details.name} onChange={(e) => this.onChange("name", e.target.value)} autoFocus/>
                        </FormField>

                        <FormField
                            displayName="Description (optional)"
                            fieldName="description"
                            errors={this.state.errors}>
                            <textarea className="Form-input full" name="description" placeholder="It's optional but oh, so helpful" value={this.state.details.description} onChange={(e) => this.onChange("description", e.target.value)} />
                        </FormField>
                    </div>

                    <div className="Form-actions">
                        <button className={cx("Button", { "Button--primary": this.state.valid })} disabled={!this.state.valid}>
                            Save
                        </button>
                        <span className="px1">or</span>
                        <a className="no-decoration text-brand text-bold" onClick={this.props.closeFn}>Cancel</a>
                        {formError}
                    </div>
                </form>
            </ModalContent>
        );
    }
}<commit_msg>Tweaked save question default mode and change behavior<commit_after>import React, { Component, PropTypes } from "react";

import FormField from "metabase/components/FormField.jsx";
import ModalContent from "metabase/components/ModalContent.jsx";

import Query from "metabase/lib/query";
import { cancelable } from "metabase/lib/promise";

import cx from "classnames";


export default class SaveQuestionModal extends Component {

    constructor(props, context) {
        super(props, context);

        const isStructured = Query.isStructured(props.card.dataset_query);

        this.state = {
            error: null,
            valid: false,
            details: {
                name: props.card.name || isStructured ? Query.generateQueryDescription(props.tableMetadata, props.card.dataset_query.query) : "",
                description: props.card.description || null,
                saveType: props.originalCard ? "overwrite" : "create"
            }
        };
    }

    static propTypes = {
        card: PropTypes.object.isRequired,
        originalCard: PropTypes.object,
        tableMetadata: PropTypes.object, // can't be required, sometimes null
        createFn: PropTypes.func.isRequired,
        saveFn: PropTypes.func.isRequired,
        closeFn: PropTypes.func.isRequired
    }

    componentDidMount() {
        this.validateForm();
    }

    componentDidUpdate() {
        this.validateForm();
    }

    componentWillUnmount() {
        if (this.requestPromise) {
            this.requestPromise.cancel();
        }
    }

    validateForm() {
        let { details } = this.state;

        let valid = true;

        // name is required
        if (!details.name) {
            valid = false;
        }

        if (this.state.valid !== valid) {
            this.setState({ valid });
        }
    }

    onChange(fieldName, fieldValue) {
        this.setState({ details: { ...this.state.details, [fieldName]: fieldValue ? fieldValue : null }});
    }

    async formSubmitted(e) {
        try {
            e.preventDefault();

            let { details } = this.state;
            let { card, originalCard, addToDashboard, createFn, saveFn } = this.props;

            card = {
                ...card,
                name: details.saveType === "overwrite" ?
                    originalCard.name :
                    details.name.trim(),
                // since description is optional, it can be null, so check for a description before trimming it
                description: details.saveType === "overwrite" ?
                    originalCard.description :
                    details.description ? details.description.trim() : null,
                public_perms: 2
            };

            if (details.saveType === "create") {
                this.requestPromise = cancelable(createFn(card, addToDashboard));
            } else if (details.saveType === "overwrite") {
                card.id = this.props.originalCard.id;
                this.requestPromise = cancelable(saveFn(card, addToDashboard));
            }

            await this.requestPromise;
            this.props.closeFn();
        } catch (error) {
            if (error && !error.isCanceled) {
                this.setState({ error: error });
            }
        }
    }

    render() {
        let { error, details } = this.state;
        var formError;
        if (error) {
            var errorMessage;
            if (error.status === 500) {
                errorMessage = "Server error encountered";
            }

            if (error.data && error.data.message) {
                errorMessage = error.data.message;
            }
            if (error.data && error.data.errors) {
                errorMessage = Object.values(error.data.errors);
            }

            // TODO: timeout display?
            if (errorMessage) {
                formError = (
                    <span className="text-error px2">{errorMessage}</span>
                );
            }
        }

        var saveOrUpdate = null;
        if (!this.props.card.id && this.props.originalCard) {
            saveOrUpdate = (
                <FormField
                    displayName="Save or Replace?"
                    fieldName="saveType"
                    errors={this.state.errors}>
                    <ul>
                        <li onClick={(e) => this.onChange("saveType", "create")}><input type="radio" name="saveType" value="create" checked={this.state.details.saveType === "create"} /> Save as a new question?</li>
                        <li onClick={(e) => this.onChange("saveType", "overwrite")}><input type="radio" name="saveType" value="overwrite" checked={this.state.details.saveType === "overwrite"} /> Replace original question, "{this.props.originalCard.name}"</li>
                    </ul>
                </FormField>
            );
        }

        let title = this.props.addToDashboard ? "First, Save Your Question" : "Save Question";

        return (
            <ModalContent
                title={title}
                closeFn={this.props.closeFn}
            >
                <form className="flex flex-column flex-full" onSubmit={(e) => this.formSubmitted(e)}>
                    <div className="Form-inputs">
                        {saveOrUpdate}

                        { details.saveType === "create" && [
                            <FormField
                                key="name"
                                displayName="Name"
                                fieldName="name"
                                errors={this.state.errors}>
                                <input className="Form-input full" name="name" placeholder="What is the name of your card?" value={this.state.details.name} onChange={(e) => this.onChange("name", e.target.value)} autoFocus/>
                            </FormField>,
                            <FormField
                                key="description"
                                displayName="Description (optional)"
                                fieldName="description"
                                errors={this.state.errors}>
                                <textarea className="Form-input full" name="description" placeholder="It's optional but oh, so helpful" value={this.state.details.description} onChange={(e) => this.onChange("description", e.target.value)} />
                            </FormField>
                        ]}
                    </div>

                    <div className="Form-actions">
                        <button className={cx("Button", { "Button--primary": this.state.valid })} disabled={!this.state.valid}>
                            Save
                        </button>
                        <span className="px1">or</span>
                        <a className="no-decoration text-brand text-bold" onClick={this.props.closeFn}>Cancel</a>
                        {formError}
                    </div>
                </form>
            </ModalContent>
        );
    }
}
{'fraction_non_alphanumeric': 0.1169803250641574, 'fraction_numerical': 0.000855431993156544, 'mean_word_length': 1.520481494789795, 'pattern_counts': {'":': 2, '<': 67, '<?xml version=': 0, '>': 77, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3829968', 'n_tokens_mistral': 3564, 'n_tokens_neox': 3417, 'n_words': 727}