<commit_before>import {autobind} from "core-decorators";
import {isEmpty} from "lodash";
import {action, observable, runInAction} from "mobx";
import * as React from "react";

import {PanelButtonsProps} from "focus-components/panel";

import {applicationStore} from "../application";
import {messageStore} from "../message";
import {FieldErrors} from "../network";

import {Field} from "./field";
import {StoreNode, toFlatValues} from "./store";
import {EntityField} from "./types";
import {createViewModel, ViewModel} from "./view-model";

import {
    autocompleteSelectFor,
    AutocompleteSelectOptions,
    autocompleteTextFor,
    AutocompleteTextOptions,
    BaseOptions,
    displayFor,
    fieldFor,
    fieldForWith,
    FieldOptions,
    selectFor,
    SelectOptions,
    stringFor,
    textFor,
    TextOptions
} from "./field-helpers";

import {form} from "./style/auto-form.css";

/** Props propre à AutoForm. */
export interface AutoFormProps {

    /** Pour ajouter une classe particulière sur le formulaire. */
    formClassName?: string;

    /** Défaut: true */
    hasEdit?: boolean;

    /** Défaut: false */
    hasDelete?: boolean;

    /** Défaut: true */
    hasForm?: boolean;

    /** Défaut: true */
    hasLoad?: boolean;

    /** L'id ne sera utilisé que par le service de chargement. */
    id?: number | string;

    /** Défaut: false */
    isEdit?: boolean;
}

/** Config de services à fournir à AutoForm. */
export interface ServiceConfig {
    [x: string]: any;
    delete?: (entity: {}) => Promise<void | number | boolean>;
    load: (id: number | string) => Promise<{}>;
    save: (entity: {}) => Promise<{}>;
}

/** Classe de base pour un composant Focus avec un formulaire. */
@autobind
export abstract class AutoForm<P, E extends StoreNode<{}>> extends React.Component<P & AutoFormProps, void> {

    // On ne peut pas injecter le contexte dans le form (héritage...) donc on va le chercher directement.
    static contextTypes = {classNames: React.PropTypes.object};
    context: {classNames: {[key: string]: {[key: string]: any}}};

    private isCustomEntity: boolean;

    readonly entity: E & ViewModel;
    hasForm = this.props.hasForm || true;
    readonly services: ServiceConfig;
    readonly storeData: E;

    @observable errors: FieldErrors = {};
    @observable isEdit = this.props.isEdit || false;
    @observable isLoading = false;

    /**
     * Initialise le formulaire.
     * @param props Les props du composant.
     * @param storeData L'EntityStoreData de base du formulaire.
     * @param services La config de services pour le formulaire ({delete?, load, save}).
     * @param entity ViewModel externe de `storeData`, s'il y a besoin d'externaliser le state interne du formulaire.
     */
    constructor(props: P, storeData: E, services: ServiceConfig, entity?: E & ViewModel) {
        super(props);
        this.storeData = storeData;
        this.services = services;
        this.entity = entity || createViewModel(storeData);
        this.isCustomEntity = entity !== undefined;
    }

    @action
    async componentWillMount() {
        this.entity.subscribe();
        let {hasLoad = true, id} = this.props;
        if (hasLoad && id) {
            this.isLoading = true;
            const data = await this.services.load(id);
            runInAction(() => {
                this.storeData.set(data);
                this.isLoading = false;
                this.onFormLoaded();
            });
        }
    }

    componentWillUnmount() {
        if (!this.isCustomEntity) {
            this.entity.unsubscribe();
        }
    }

    /** Change le mode du formulaire. */
    @action
    toggleEdit(isEdit: boolean) {
        this.isEdit = isEdit;
        this.errors = {};
        if (isEdit) {
            applicationStore.changeMode("edit", "consult");
        } else {
            this.entity.reset();
            applicationStore.changeMode("consult", "edit");
        }
    }

    /** Appelle le service de suppression. */
    @action
    async delete() {
        if (this.services.delete) {
            this.isLoading = true;
            await this.services.delete(toFlatValues(this.entity));
            messageStore.addSuccessMessage("detail.deleted");
            runInAction(() => {
                this.isLoading = false;
                this.storeData.clear();
                this.onFormDeleted();
            });
        }
    }

    /** Appelle le service de sauvegarde. */
    @action
    async save() {
        this.errors = {};
        if (this.validate()) {
            this.isLoading = true;
            try {
                const data = await this.services.save(toFlatValues(this.entity));
                messageStore.addSuccessMessage("detail.saved");
                runInAction(() => {
                    this.isLoading = false;
                    this.isEdit = false;
                    this.storeData.set(data);
                    this.onFormSaved();
                });
            } catch (e) {
                messageStore.addErrorMessage("detail.error");
                runInAction(() => this.errors = e.fields);
            }
        }
    }

    onFormDeleted() {
        // A éventuellement surcharger.
    }

    onFormLoaded() {
        // A éventuellement surcharger.
    }

    onFormSaved() {<commit_msg>Retrait message d'erreur erreur save form<commit_after>import {autobind} from "core-decorators";
import {isEmpty} from "lodash";
import {action, observable, runInAction} from "mobx";
import * as React from "react";

import {PanelButtonsProps} from "focus-components/panel";

import {applicationStore} from "../application";
import {messageStore} from "../message";
import {FieldErrors} from "../network";

import {Field} from "./field";
import {StoreNode, toFlatValues} from "./store";
import {EntityField} from "./types";
import {createViewModel, ViewModel} from "./view-model";

import {
    autocompleteSelectFor,
    AutocompleteSelectOptions,
    autocompleteTextFor,
    AutocompleteTextOptions,
    BaseOptions,
    displayFor,
    fieldFor,
    fieldForWith,
    FieldOptions,
    selectFor,
    SelectOptions,
    stringFor,
    textFor,
    TextOptions
} from "./field-helpers";

import {form} from "./style/auto-form.css";

/** Props propre à AutoForm. */
export interface AutoFormProps {

    /** Pour ajouter une classe particulière sur le formulaire. */
    formClassName?: string;

    /** Défaut: true */
    hasEdit?: boolean;

    /** Défaut: false */
    hasDelete?: boolean;

    /** Défaut: true */
    hasForm?: boolean;

    /** Défaut: true */
    hasLoad?: boolean;

    /** L'id ne sera utilisé que par le service de chargement. */
    id?: number | string;

    /** Défaut: false */
    isEdit?: boolean;
}

/** Config de services à fournir à AutoForm. */
export interface ServiceConfig {
    [x: string]: any;
    delete?: (entity: {}) => Promise<void | number | boolean>;
    load: (id: number | string) => Promise<{}>;
    save: (entity: {}) => Promise<{}>;
}

/** Classe de base pour un composant Focus avec un formulaire. */
@autobind
export abstract class AutoForm<P, E extends StoreNode<{}>> extends React.Component<P & AutoFormProps, void> {

    // On ne peut pas injecter le contexte dans le form (héritage...) donc on va le chercher directement.
    static contextTypes = {classNames: React.PropTypes.object};
    context: {classNames: {[key: string]: {[key: string]: any}}};

    private isCustomEntity: boolean;

    readonly entity: E & ViewModel;
    hasForm = this.props.hasForm || true;
    readonly services: ServiceConfig;
    readonly storeData: E;

    @observable errors: FieldErrors = {};
    @observable isEdit = this.props.isEdit || false;
    @observable isLoading = false;

    /**
     * Initialise le formulaire.
     * @param props Les props du composant.
     * @param storeData L'EntityStoreData de base du formulaire.
     * @param services La config de services pour le formulaire ({delete?, load, save}).
     * @param entity ViewModel externe de `storeData`, s'il y a besoin d'externaliser le state interne du formulaire.
     */
    constructor(props: P, storeData: E, services: ServiceConfig, entity?: E & ViewModel) {
        super(props);
        this.storeData = storeData;
        this.services = services;
        this.entity = entity || createViewModel(storeData);
        this.isCustomEntity = entity !== undefined;
    }

    @action
    async componentWillMount() {
        this.entity.subscribe();
        let {hasLoad = true, id} = this.props;
        if (hasLoad && id) {
            this.isLoading = true;
            const data = await this.services.load(id);
            runInAction(() => {
                this.storeData.set(data);
                this.isLoading = false;
                this.onFormLoaded();
            });
        }
    }

    componentWillUnmount() {
        if (!this.isCustomEntity) {
            this.entity.unsubscribe();
        }
    }

    /** Change le mode du formulaire. */
    @action
    toggleEdit(isEdit: boolean) {
        this.isEdit = isEdit;
        this.errors = {};
        if (isEdit) {
            applicationStore.changeMode("edit", "consult");
        } else {
            this.entity.reset();
            applicationStore.changeMode("consult", "edit");
        }
    }

    /** Appelle le service de suppression. */
    @action
    async delete() {
        if (this.services.delete) {
            this.isLoading = true;
            await this.services.delete(toFlatValues(this.entity));
            messageStore.addSuccessMessage("detail.deleted");
            runInAction(() => {
                this.isLoading = false;
                this.storeData.clear();
                this.onFormDeleted();
            });
        }
    }

    /** Appelle le service de sauvegarde. */
    @action
    async save() {
        this.errors = {};
        if (this.validate()) {
            this.isLoading = true;
            try {
                const data = await this.services.save(toFlatValues(this.entity));
                messageStore.addSuccessMessage("detail.saved");
                runInAction(() => {
                    this.isLoading = false;
                    this.isEdit = false;
                    this.storeData.set(data);
                    this.onFormSaved();
                });
            } catch (e) {
                runInAction(() => this.errors = e.fields);
            }
        }
    }

    onFormDeleted() {
        // A éventuellement surcharger.
    }

    onFormLoaded() {
        // A éventuellement surcharger.
    }

    onFormSaved() {
{'fraction_non_alphanumeric': 0.1260836788541274, 'fraction_numerical': 0.0, 'mean_word_length': 2.213139570087799, 'pattern_counts': {'":': 0, '<': 15, '<?xml version=': 0, '>': 29, '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': '629278', 'n_tokens_mistral': 3033, 'n_tokens_neox': 2974, 'n_words': 798}