<|start_filename|>web/components/sidebar.js<|end_filename|>
import React, { Component } from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import Icon from './icon';

export default class Sidebar extends Component {
  constructor(props) {
    super(props);

    this.state = {
      options: {}
    };
  }

  handleInputChange = (event) => {
    const target = event.target;
    const value = target.type === 'checkbox' ? target.checked : target.value;
    const name = target.name;

    this.props.onChange({ [name]: value });
  };

  render() {
    let {
      version,
      versions,
      options: {
        line_length,
        skip_string_normalization,
        py36,
        pyi,
        fast
      }
    } = this.props;

    let latestUrl = `https://github.com/psf/black/commit/${versions.main}`;
    let stableUrl = `https://pypi.org/project/black/${versions.stable}/`;

    return (
      <div
        className={classNames('w-1/4', {
          hidden: !this.props.visible
        })}>
        <div className="p-4">
          <div className="flex flex-wrap">
            <div className="w-full px-3 mb-6">
              <label className="block uppercase tracking-wide text-xs font-bold mb-2">
                Version
              </label>
              <div className="inline-block relative w-full mb-3">
                <select
                  name="version"
                  className="block appearance-none w-full bg-white border border-grey-darkest px-4 py-2 pr-8 rounded shadow leading-tight focus:outline-none focus:shadow-outline"
                  value={version}
                  onChange={this.handleInputChange}>
                  <option value="stable">Stable</option>
                  <option value="main">Main</option>
                </select>
                <div className="pointer-events-none absolute pin-y pin-r flex items-center px-2">
                  <Icon icon="chevron-down" />
                </div>
              </div>
              <p className="text-grey-dark text-xs italic">
                Stable:{' '}
                <a target="_blank" className="text-grey-dark" href={stableUrl}>
                  {versions.stable}
                </a>{' '}
                | Main:{' '}
                <a target="_blank" className="text-grey-dark" href={latestUrl}>
                  @{versions.main}
                </a>
              </p>
            </div>

            <div className="w-full px-3 mb-6">
              <label className="block uppercase tracking-wide text-xs font-bold mb-2">
                Line Length
              </label>
              <input
                type="text"
                name="line_length"
                className="appearance-none block w-full border border-grey-darkest rounded py-3 px-4 mb-3 leading-tight"
                value={line_length}
                onChange={this.handleInputChange}
              />
              <p className="text-grey-dark text-xs italic">
                The recommended line length in Black is{' '}
                <a
                  href="http://black.readthedocs.io/en/latest/the_black_code_style.html#line-length"
                  target="_blank"
                  className="text-grey-dark">
                  88 characters
                </a>
              </p>
            </div>

            <div className="w-full px-3 mb-6">
              <label className="block uppercase tracking-wide text-xs font-bold mb-2">
                Other options
              </label>

              <label className="block text-grey-dark">
                <input
                  type="checkbox"
                  name="fast"
                  className="mr-2 leading-tight"
                  checked={fast}
                  value={fast}
                  onChange={this.handleInputChange}
                />
                <span className="text-sm">
                  Skip temporary sanity checks.
                </span>
              </label>

              <label className="block text-grey-dark">
                <input
                  type="checkbox"
                  name="skip_string_normalization"
                  className="mr-2 leading-tight"
                  checked={skip_string_normalization}
                  value={skip_string_normalization}
                  onChange={this.handleInputChange}
                />
                <span className="text-sm">
                  Don't normalize string quotes or prefixes.
                </span>
              </label>

              <label className="block text-grey-dark">
                <input
                  type="checkbox"
                  name="py36"
                  className="mr-2 leading-tight"
                  checked={py36}
                  value={py36}
                  onChange={this.handleInputChange}
                />
                <span className="text-sm">
                  Allow using Python 3.6-only syntax
                </span>
              </label>

              <label className="block text-grey-dark">
                <input
                  type="checkbox"
                  name="pyi"
                  className="mr-2 leading-tight"
                  checked={pyi}
                  value={pyi}
                  onChange={this.handleInputChange}
                />
                <span className="text-sm">Format typing stubs</span>
              </label>
            </div>
          </div>
        </div>
      </div>
    );
  }
}

Sidebar.propTypes = {
  visible: PropTypes.bool,
  versions: PropTypes.object,
  version: PropTypes.string,
  options: PropTypes.object,
  onChange: PropTypes.func
};

Sidebar.defaultProps = {
  visible: false,
  versions: {},
  version: null,
  options: {},
  onChange: null
};


<|start_filename|>web/pages/index.js<|end_filename|>
import 'isomorphic-unfetch';
import debounce from 'awesome-debounce-promise';
import React from 'react';
import Link from 'next/link';
import Router from 'next/router';
import dynamic from 'next/dynamic';
import ReactGA from 'react-ga';
import classNames from 'classnames';
import Head from '../components/head';
import Header from '../components/header';
import Sidebar from '../components/sidebar';
import Icon from '../components/icon';
import Spinner from '../components/spinner';

const Editor = dynamic(import('../components/editor'), { ssr: false });

const STABLE_URL = 'https://1rctyledh3.execute-api.us-east-1.amazonaws.com/dev';
const MAIN_URL = 'https://jobmcemp35.execute-api.us-east-1.amazonaws.com/dev';

async function getVersion(url) {
  let res = await (await fetch(`${url}/version`)).json();
  return res.version;
}

export default class extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      isLoading: false,
      isSidebarVisible: false,
      source: props.source,
      formatted: props.formatted,
      options: props.options,
      version: props.version,
      versions: props.versions,
      state: props.state,
      issueLink: props.issueLink
    };
  }

  static async getInitialProps({ query }) {
    let mainVersion;
    let stableVersion;
    let currentVersion = query.version || 'stable';
    let url = currentVersion === 'main' ? MAIN_URL : STABLE_URL;

    let json = await (await fetch(
      `${url}${query.state ? `?state=${query.state}` : ''}`
    )).json();

    if (currentVersion === 'main') {
      mainVersion = json.version;
      stableVersion = await getVersion(STABLE_URL);
    } else {
      stableVersion = json.version;
      mainVersion = await getVersion(MAIN_URL);
    }

    return {
      source: json.source_code,
      formatted: json.formatted_code,
      options: json.options,
      state: json.state,
      issueLink: json.issue_link,
      version: currentVersion,
      versions: {
        stable: stableVersion,
        main: mainVersion
      }
    };
  }

  componentDidMount() {
    this.updateStateParam();
    ReactGA.initialize('UA-37217294-8');
    ReactGA.set({ anonymizeIp: true });
    ReactGA.pageview(window.location.pathname);
  }

  updateStateParam() {
    let href = `/?version=${this.state.version}&state=${this.state.state}`;
    Router.replace(href, href, { shallow: true });
  }

  handleToggleSidebar = () => {
    this.setState((prevState) => ({
      isSidebarVisible: !prevState.isSidebarVisible
    }));
  };

  handleSourceUpdate = async (value) => {
    this.setState(() => ({
      source: value,
      isLoading: true
    }));

    const json = await this.fetchFormat();

    this.setState(() => ({
      isLoading: false,
      source: json.source_code,
      formatted: json.formatted_code,
      options: json.options,
      state: json.state,
      issueLink: json.issue_link
    }));

    this.updateStateParam();
  };

  handleOptionsUpdate = async (value) => {
    this.setState((prevState) => {
      if (value.version) {
        return {
          isLoading: true,
          version: value.version
        };
      }

      return {
        isLoading: true,
        options: Object.assign({}, prevState.options, value)
      };
    });

    const json = await this.fetchFormat();

    this.setState(() => ({
      isLoading: false,
      source: json.source_code,
      formatted: json.formatted_code,
      options: json.options,
      state: json.state,
      issueLink: json.issue_link
    }));

    this.updateStateParam();
  };

  fetchFormat = debounce(
    () =>
      fetch(this.state.version === 'stable' ? STABLE_URL : MAIN_URL, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          source: this.state.source,
          options: this.state.options
        })
      }).then((res) => res.json()),
    700
  );

  render() {
    let currentVersion = this.state.versions[this.state.version];

    if (this.state.version === 'stable') {
      currentVersion = `v${currentVersion}`;
    } else {
      currentVersion = `@${currentVersion}`;
    }

    return (
      <div>
        <Head title="Black Playground" />
        <div className="flex flex-col h-screen">
          <Header version={currentVersion} />

          <div className="flex flex-1 flex-col">
            <div className="flex flex-1 min-h-0">
              <Sidebar
                version={this.state.version}
                versions={this.state.versions}
                options={this.state.options}
                visible={this.state.isSidebarVisible}
                onChange={this.handleOptionsUpdate}
              />

              <div className="flex flex-1">
                <div className="flex flex-1 relative">
                  <Editor
                    value={this.state.source}
                    onChange={this.handleSourceUpdate}
                  />
                </div>
                <div className="flex flex-1 relative">
                  {this.state.isLoading ? (
                    <div className="flex items-center justify-center w-full ace-tomorrow-night">
                      <Spinner />
                    </div>
                  ) : (
                    <Editor value={this.state.formatted} readOnly={true} />
                  )}
                </div>
              </div>
            </div>

            <div className="flex justify-between content-center items-center p-4">
              <button
                className={classNames('text-sm inline-flex items-center', {
                  'text-black': this.state.isSidebarVisible,
                  'hover:text-grey-dark': this.state.isSidebarVisible,
                  'text-grey-dark': !this.state.isSidebarVisible,
                  'hover:text-black': !this.state.isSidebarVisible
                })}
                onClick={this.handleToggleSidebar}>
                <Icon icon="cog" />
              </button>
              <div className="flex text-right">
                <div className="flex text-right">
                  <Link href={this.state.issueLink}>
                    <a className="bg-transparent text-xs py-1 text-black font-bold no-underline hover:underline">
                      Report issue
                    </a>
                  </Link>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
    );
  }
}
