Source: components/traits/Queryable.js

import React from 'react';
import ReactDOM from 'react-dom';
import Trait from './Trait';
import { get, camelCase, snakeCase } from 'lodash';
import { app_get, snake_case, def } from '../../helpers';
import Shell from '../Shell';
import ReactSync from '../../ReactSync';
import collect from '../../collect.js';
import axios from '../../fetchClient';
import { dispatch, on } from '../../Event.js';


window.Find = (dotstring) => {
	return get(ReactSyncAppData.page_data.state, dotstring);
};



/**
@kind mixin
@extends Trait
*/
class Queryable extends Trait{

	static repo = ReactSyncAppData.page_data.state;

	static hasBeenKeyed = false;

	static setKeyed(){

	}



/**
 * (STATIC) - Query the data store and return the model with the supplied id
 * @static
 */
	static find(id){
		const { plural, repo } = this;
		const dotstring = `${plural}.${id}`;
		return app_get(dotstring);
	}


/**
 * (STATIC) -
 * @static
 */
	static where(...args){
		this.refresher = React.createRef();
		return collect(appGet(this.plural)).where(...args);
	}


	/**
	 * (STATIC) - Return all models from the data store
	 * @static
	 */


	static list(additional_props = {}){

		return <Shell url={this.plural_url} Model={this} {...additional_props} />
	}


	/** */
	static all(additional_props = {}){
    	debugger;
		this.refresher = React.createRef();
		const plural = this.plural;
		let items = app_get('state.'+plural) || app_get('state.'+ snakeCase(plural)) || app_get(plural) || app_get(snakeCase(plural));
// debugger
		if(!items) return null;
		if(!('map' in items)) items = items.data;
        let els = items.map(e => {
			let props = {...e, ...additional_props};
			let ThisModel = this;
			return <ThisModel refresher={this.refresher} key={`${plural}${e.id}`} {...props} />;
        });

		return <>{els}</>
	}

	/** */
	static firstWhere(a, b){

        return null;

		let ThisModel = this;
		let props = {...this.props, ...additional_props};
		return <ThisModel refresher={this.refresher} key={`${this.plural}${this.props.id}`} {...props} />;
	}

	/** */
	static first(additional_props = {}){
		this.refresher = React.createRef();
		const plural = this.plural;
		let items = app_get('state.'+plural) || app_get('state.'+ snakeCase(plural)) || app_get(plural) || app_get(snakeCase(plural));
		if(!items.length) return null;
		let e = items[0];
		let props = {...e, ...additional_props};
		let ThisModel = this;
		return <ThisModel refresher={this.refresher} key={`${this.plural}${e.id}`} {...props} />;
	}


	/**
	 */
	get save_path(){
		return this.api_url;
	}

	/**
	 * Store the model's state back to the database
	 */
	save(callback = false){
		const save_path = 	this.save_path || this.calculatedProperties.api_url;
		axios.put(save_path, this.filterMutable(this.state))
			.then(response => {
				this.refresh();
				react_sync_notification('Saved');
				if(callback) callback();
			})
			.catch(err => {
				console.error(err);
				react_sync_notification({text: 'An error occurred', level: 'danger'});
			});
	}


	/** */
	static get create_path(){
		return this.plural_url;
	}



	/**
	 * Create a new model and insert into the database
	 */
	static create(initialProps = {}){
    	return new Promise((resolve, reject) => {
    		axios.post(this.create_path, initialProps)
    			.then(response => {
        			const redirect_url = this.response_is_redirect(response);

    				if(redirect_url){
    					history.pushState(response.data, "", redirect_url);
    					this.refresh_static();
    				}
    				else{
    					this.refresh_static();
    				}

    				react_sync_notification('Saved');

    				resolve(response.data);

    				dispatch('model_created', new this(response.data));
    			})
    			.catch(err => {
    				console.error(err);
    				react_sync_notification({text: 'An error occurred', level: 'danger'});
    				reject(err);
    			});

    	});
	}

	/**
	 * Create a new model instance and store it to the database
	 * @todo complete this method
	 */


	/** */
	get delete_path(){

		return this.api_url;
	}

	/**
	 * Delete a model
	 */
	delete(callback = false){
    	return new Promise((resolve, reject) => {
    		axios.delete(this.delete_path)
    			.then(response => {
        			const redirect_url = this.constructor.response_is_redirect(response);
    				if(redirect_url){
    					history.pushState(response.data, "", redirect_url);
    					this.refresh();
    				}
    				else{
    					this.refresh();
    				}

    				react_sync_notification('Deleted');
    				dispatch('model_deleted', response);
    				resolve(response);
    				if(typeof callback === 'function'){
	    				callback(response);
    				}

    			})
    			.catch(err => {
    				console.log(err);
    				react_sync_notification({text: 'An error occurred', level: 'danger'});
    				reject(err);
    			});
			});
	}


	/** */
	static refresh_static(){
		Shell.cache = {};
		ReactSync.getInstance().update();
	}


	/** */
	refresh(){
		Shell.cache = {};

		if(this.props.refresh) this.props.refresh()
		else{
			ReactSync.getInstance().update();
		}

	}

    static response_is_redirect(response){
    	const { href } = window.location;
    	const { responseURL } = response.request;
    	const { url } = response.config;
    	console.log(url, responseURL);
    	if(responseURL !== url && href !== responseURL){
        	return responseURL;
    	}
    	return false;
    }



}


export default Queryable;