<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.app',
  name: 'AppConfig',

  properties: [
    {
      class: 'String',
      name: 'name'
    },
    {
      class: 'String',
      name: 'version'
    },
    {
      class: 'String',
      name: 'privacy',
      value: 'Privacy Policy'
    },
    {
      class: 'String',
      name: 'privacyUrl'
    },
    {
      class: 'String',
      name: 'copyright'
    },
    {
      class: 'String',
      name: 'url',
      value: 'http://localhost:8080/'
    },
    {
      class: 'String',
      name: 'urlLabel',
      value: 'FOAM Powered'
    },
    {
      class: 'String',
      name: 'termsAndCondLabel',
      value: 'Terms and Conditions'
    },
    {
      class: 'String',
      name: 'termsAndCondLink'
    },
    {
      class: 'Enum',
      of: 'foam.nanos.app.Mode',
      name: 'mode'
    },
    {
      class: 'String',
      name: 'appLink',
      value: 'https://www.apple.com/lae/ios/app-store/'
    },
    {
      class: 'String',
      name: 'playLink',
      value: 'https://play.google.com/store?hl=en'
    },
    {
      class: 'Boolean',
      name: 'forceHttps',
      value: false
    },
    {
      class: 'String',
      name: 'supportEmail'
    },
    {
      class: 'String',
      name: 'defaultSpid'
    }
  ]
});
<commit_msg>add new prop to support set external Url<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.app',
  name: 'AppConfig',

  properties: [
    {
      class: 'String',
      name: 'name'
    },
    {
      class: 'String',
      name: 'version'
    },
    {
      class: 'String',
      name: 'privacy',
      value: 'Privacy Policy'
    },
    {
      class: 'String',
      name: 'privacyUrl'
    },
    {
      class: 'String',
      name: 'copyright'
    },
    {
      class: 'String',
      name: 'url',
      value: 'http://localhost:8080/'
    },
    {
      class: 'String',
      name: 'urlLabel',
      value: 'FOAM Powered'
    },
    {
      class: 'String',
      name: 'termsAndCondLabel',
      value: 'Terms and Conditions'
    },
    {
      class: 'String',
      name: 'termsAndCondLink'
    },
    {
      class: 'Enum',
      of: 'foam.nanos.app.Mode',
      name: 'mode'
    },
    {
      class: 'String',
      name: 'appLink',
      value: 'https://www.apple.com/lae/ios/app-store/'
    },
    {
      class: 'String',
      name: 'playLink',
      value: 'https://play.google.com/store?hl=en'
    },
    {
      class: 'Boolean',
      name: 'forceHttps',
      value: false
    },
    {
      class: 'String',
      name: 'supportEmail'
    },
    {
      class: 'String',
      name: 'defaultSpid'
    },
    {
      class: 'String',
      name: 'externalUrl'
    }
  ]
});
<|endoftext|>


<commit_before>import React, { Component, Fragment } from 'react'
import {
  ThemeProvider,
  Container,
  Box,
  Button,
  Flex,
  Heading,
  Text,
  Badge
} from '@hackclub/design-system'
import { AutoSaver, Field, Form } from 'components/Forms'
import Login from 'components/apply/Login'
import Table from 'components/Table'
import LogoutButton from 'components/apply/LogoutButton'
import LoadingAnimation from 'components/LoadingAnimation'
import InterviewForm from 'components/admin/InterviewForm'
import RejectionForm from 'components/admin/RejectionForm'
import AcceptanceForm from 'components/admin/AcceptanceForm'
import Information from 'components/admin/Information'
import NotesForm from 'components/admin/NotesForm'
import { Formik } from 'formik'
import api from 'api'

class Dashboard extends Component {
  constructor(props) {
    super(props)
    this.state = { status: 'loading' }
    this.updateApplicationList = this.updateApplicationList.bind(this)
  }

  componentDidMount() {
    const authToken = window.localStorage.getItem('authToken')
    this.setState({ authToken })

    api
      .get('v1/new_club_applications', { authToken })
      .then(json => {
        let clubApplications = {}
        json.forEach(app => {
          if (app.submitted_at) {
            clubApplications[app.id] = app
          }
        })
        this.setState({
          status: 'success',
          clubApplications
        })
      })
      .catch(e => {
        if (e.status === 401) {
          this.setState({ status: 'needsToAuth' })
        } else {
          this.setState({ status: 'error' })
        }
      })
  }

  updateApplicationList(updatedApplication) {
    const { clubApplications } = this.state
    clubApplications[updatedApplication.id] = updatedApplication

    this.setState({ clubApplications })
  }

  pointOfContact(application) {
    const profile = application.leader_profiles.find(
      profile =>
        profile.user && profile.user.id === application.point_of_contact_id
    )

    return profile && profile.user.email
  }

  active(application, prop) {
    const { selection, selectType } = this.state
    return selection && selection.id === application.id && selectType === prop
  }

  render() {
    const {
      authToken,
      status,
      clubApplications,
      selection,
      selectType
    } = this.state
    switch (status) {
      case 'loading':
        return <LoadingAnimation />
      case 'needsToAuth':
        return <Login userType="admin" />
      case 'success':
        return (
          <Container maxWidth={80} p={[3, 4]}>
            <Flex
              flexDirection={['column', 'row']}
              justify="space-between"
              align="center"
              wrap
            >
              <Heading.h1 f={[5, 6]}>Dashboard</Heading.h1>
              <LogoutButton />
            </Flex>
            <Heading.h2 color="muted" f={4} mt={2} regular>
              Hello, it’s{' '}
              {new Date().toLocaleDateString('en-us', { weekday: 'long' })}
              {'. '}You’re doing great.
            </Heading.h2>
            <Flex justify="center" mt={[3, 4]}>
              <Table
                headers={[
                  'ID',
                  'Name',
                  'POC',
                  'Interview',
                  'Notes',
                  'Rejected',
                  'Info',
                  'Accepted'
                ]}
                rows={Object.values(clubApplications).map(application => ({
                  ID: <Badge bg="info" children={application.id} />,
                  Name: application.high_school_name,
                  POC: this.pointOfContact(application),
                  Interview: (
                    <Button
                      bg="info"
                      inverted={!this.active(application, 'interview')}
                      disabled={!application.submitted_at}
                      onClick={() => {
                        this.setState({
                          selection: application,
                          selectType: 'interview'
                        })
                      }}
                      children="✍"
                    />
                  ),
                  Notes: (
                    <Button
                      bg="info"
                      inverted={!this.active(application, 'notes')}
                      disabled={!application.submitted_at}
                      onClick={() => {
                        this.setState({
                          selection: application,
                          selectType: 'notes'
                        })
                      }}
                      children="✍"
                    />
                  ),
                  Rejected: (
                    <Button
                      bg="info"
                      inverted={!this.active(application, 'rejected')}
                      disabled={!application.submitted_at}
                      onClick={() => {
                        this.setState({
                          selection: application,
                          selectType: 'rejected'
                        })
                      }}
                      children="✍"
                    />
                  ),
                  Info: (
                    <Button
                      bg="info"
                      inverted={
                        !(
                          selection &&
                          selection.id === application.id &&
                          selectType === 'info'
                        )
                      }
                      disabled={!application.submitted_at}
                      onClick={() => {
                        this.setState({
                          selection: application,
                          selectType: 'info'
                        })
                      }}
                      children="✍"
                    />
                  ),
                  Accepted: (
                    <Button
                      bg="info"
                      inverted={
                        !(
                          selection &&
                          selection.id === application.id &&
                          selectType === 'accepted'
                        )
                      }
                      disabled={!application.submitted_at}
                      onClick={() => {
                        this.setState({
                          selection: application,
                          selectType: 'accepted'
                        })
                      }}
                      children="✍"
                    />
                  )
                }))}
              />
              {selection && (
                <Flex
                  flexDirection="column"
                  flex="1 1 auto"
                  ml={[null, 4]}
                  style={{ minWidth: '18rem' }}
                >
                  {selectType === 'rejected' ? (
                    <RejectionForm
                      authToken={authToken}
                      application={selection}
                      updateApplicationList={this.updateApplicationList}
                    />
                  ) : null}
                  {selectType === 'notes' ? (
                    <NotesForm authToken={authToken} application={selection} />
                  ) : null}
                  {selectType === 'interview' ? (
                    <InterviewForm
                      authToken={authToken}
                      application={selection}
                      updateApplicationList={this.updateApplicationList}
                    />
                  ) : null}
                  {selectType === 'info' ? (
                    <Information application={selection} />
                  ) : null}
                  {selectType === 'accepted' ? (
                    <AcceptanceForm
                      authToken={authToken}
                      application={selection}
                      updateApplicationList={this.updateApplicationList}
                    />
                  ) : null}
                </Flex>
              )}
            </Flex>
          </Container>
        )
      default:
        return (
          <Text color="error" py={3} align="center">
            🚨 Something terrible has happened 🚨
          </Text>
        )
    }
  }
}
export default () => (
  <ThemeProvider>
    <Dashboard />
  </ThemeProvider>
)
<commit_msg>Color code application stages<commit_after>import React, { Component, Fragment } from 'react'
import {
  ThemeProvider,
  Container,
  Box,
  Button,
  Flex,
  Heading,
  Text,
  Badge
} from '@hackclub/design-system'
import { AutoSaver, Field, Form } from 'components/Forms'
import Login from 'components/apply/Login'
import Table from 'components/Table'
import LogoutButton from 'components/apply/LogoutButton'
import LoadingAnimation from 'components/LoadingAnimation'
import InterviewForm from 'components/admin/InterviewForm'
import RejectionForm from 'components/admin/RejectionForm'
import AcceptanceForm from 'components/admin/AcceptanceForm'
import Information from 'components/admin/Information'
import NotesForm from 'components/admin/NotesForm'
import { Formik } from 'formik'
import api from 'api'

class Dashboard extends Component {
  constructor(props) {
    super(props)
    this.state = { status: 'loading' }
    this.updateApplicationList = this.updateApplicationList.bind(this)
  }

  componentDidMount() {
    const authToken = window.localStorage.getItem('authToken')
    this.setState({ authToken })

    api
      .get('v1/new_club_applications', { authToken })
      .then(json => {
        let clubApplications = {}
        json.forEach(app => {
          if (app.submitted_at) {
            clubApplications[app.id] = app
          }
        })
        this.setState({
          status: 'success',
          clubApplications
        })
      })
      .catch(e => {
        if (e.status === 401) {
          this.setState({ status: 'needsToAuth' })
        } else {
          this.setState({ status: 'error' })
        }
      })
  }

  updateApplicationList(updatedApplication) {
    const { clubApplications } = this.state
    clubApplications[updatedApplication.id] = updatedApplication

    this.setState({ clubApplications })
  }

  pointOfContact(application) {
    const profile = application.leader_profiles.find(
      profile =>
        profile.user && profile.user.id === application.point_of_contact_id
    )

    return profile && profile.user.email
  }

  active(application, prop) {
    const { selection, selectType } = this.state
    return selection && selection.id === application.id && selectType === prop
  }

  badgeColor(application) {
    if (application.accepted_at) {
      return 'success'
    } else if (application.rejected_at) {
      return 'primary'
    } else if (application.interviewed_at) {
      return 'accent'
    } else {
      return 'info'
    }
  }

  render() {
    const {
      authToken,
      status,
      clubApplications,
      selection,
      selectType
    } = this.state
    switch (status) {
      case 'loading':
        return <LoadingAnimation />
      case 'needsToAuth':
        return <Login userType="admin" />
      case 'success':
        return (
          <Container maxWidth={80} p={[3, 4]}>
            <Flex
              flexDirection={['column', 'row']}
              justify="space-between"
              align="center"
              wrap
            >
              <Heading.h1 f={[5, 6]}>Dashboard</Heading.h1>
              <LogoutButton />
            </Flex>
            <Heading.h2 color="muted" f={4} mt={2} regular>
              Hello, it’s{' '}
              {new Date().toLocaleDateString('en-us', { weekday: 'long' })}
              {'. '}You’re doing great.
            </Heading.h2>
            <Flex mt={[3, 4]}>
              <Badge mr={3} bg="primary">Rejected</Badge>
              <Badge mr={3} bg="accent">Awaiting Interview</Badge>
              <Badge mr={3} bg="info">Awaiting Decision</Badge>
              <Badge mr={3} bg="success">Accepted</Badge>
            </Flex>
            <Flex justify="center" mt={[3, 4]}>
              <Table
                headers={[
                  'ID',
                  'Name',
                  'POC',
                  'Interview',
                  'Notes',
                  'Rejected',
                  'Info',
                  'Accepted'
                ]}
                rows={Object.values(clubApplications).map(application => ({
                  ID: <Badge bg={this.badgeColor(application)} children={application.id} />,
                  Name: application.high_school_name,
                  POC: this.pointOfContact(application),
                  Interview: (
                    <Button
                      bg="info"
                      inverted={!this.active(application, 'interview')}
                      disabled={!application.submitted_at}
                      onClick={() => {
                        this.setState({
                          selection: application,
                          selectType: 'interview'
                        })
                      }}
                      children="✍"
                    />
                  ),
                  Notes: (
                    <Button
                      bg="info"
                      inverted={!this.active(application, 'notes')}
                      disabled={!application.submitted_at}
                      onClick={() => {
                        this.setState({
                          selection: application,
                          selectType: 'notes'
                        })
                      }}
                      children="✍"
                    />
                  ),
                  Rejected: (
                    <Button
                      bg="info"
                      inverted={!this.active(application, 'rejected')}
                      disabled={!application.submitted_at}
                      onClick={() => {
                        this.setState({
                          selection: application,
                          selectType: 'rejected'
                        })
                      }}
                      children="✍"
                    />
                  ),
                  Info: (
                    <Button
                      bg="info"
                      inverted={
                        !(
                          selection &&
                          selection.id === application.id &&
                          selectType === 'info'
                        )
                      }
                      disabled={!application.submitted_at}
                      onClick={() => {
                        this.setState({
                          selection: application,
                          selectType: 'info'
                        })
                      }}
                      children="✍"
                    />
                  ),
                  Accepted: (
                    <Button
                      bg="info"
                      inverted={
                        !(
                          selection &&
                          selection.id === application.id &&
                          selectType === 'accepted'
                        )
                      }
                      disabled={!application.submitted_at}
                      onClick={() => {
                        this.setState({
                          selection: application,
                          selectType: 'accepted'
                        })
                      }}
                      children="✍"
                    />
                  )
                }))}
              />
              {selection && (
                <Flex
                  flexDirection="column"
                  flex="1 1 auto"
                  ml={[null, 4]}
                  style={{ minWidth: '18rem' }}
                >
                  {selectType === 'rejected' ? (
                    <RejectionForm
                      authToken={authToken}
                      application={selection}
                      updateApplicationList={this.updateApplicationList}
                    />
                  ) : null}
                  {selectType === 'notes' ? (
                    <NotesForm authToken={authToken} application={selection} />
                  ) : null}
                  {selectType === 'interview' ? (
                    <InterviewForm
                      authToken={authToken}
                      application={selection}
                      updateApplicationList={this.updateApplicationList}
                    />
                  ) : null}
                  {selectType === 'info' ? (
                    <Information application={selection} />
                  ) : null}
                  {selectType === 'accepted' ? (
                    <AcceptanceForm
                      authToken={authToken}
                      application={selection}
                      updateApplicationList={this.updateApplicationList}
                    />
                  ) : null}
                </Flex>
              )}
            </Flex>
          </Container>
        )
      default:
        return (
          <Text color="error" py={3} align="center">
            🚨 Something terrible has happened 🚨
          </Text>
        )
    }
  }
}
export default () => (
  <ThemeProvider>
    <Dashboard />
  </ThemeProvider>
)
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.script',
  name: 'Script',

  implements: ['foam.nanos.auth.EnabledAware'],

  requires: [
    'foam.nanos.script.ScriptStatus',
    'foam.nanos.notification.notifications.ScriptRunNotification'
  ],

  imports: [
    'notificationDAO',
    'scriptDAO',
    'user'
  ],

  javaImports: [
    'bsh.EvalError',
    'bsh.Interpreter',
    'foam.core.*',
    'foam.dao.*',
    'foam.mlang.predicate.Predicate',
    'foam.nanos.auth.*',
    'foam.nanos.pm.PM',
    'foam.nanos.session.Session',
    'java.io.ByteArrayOutputStream',
    'java.io.PrintStream',
    'java.util.Date',
    'java.util.List',
    'static foam.mlang.MLang.*',
  ],

  tableColumns: [
    'id', 'server', 'description', 'lastDuration', 'status', 'run'
  ],

  searchColumns: ['id', 'description'],

  constants: [
    {
      name: 'MAX_OUTPUT_CHARS',
      type: 'Integer',
      value: 20000
    },
    {
      name: 'MAX_NOTIFICATION_OUTPUT_CHARS',
      type: 'Integer',
      value: 200
    }
  ],

  properties: [
    {
      class: 'String',
      name: 'id',
      tableCellFormatter: function(value) {
        this.start()
          .style({
            'overflow': 'hidden',
            'max-width': '25ch',
            'min-width': '25ch',
            'text-overflow': 'ellipsis'
          }).add(value)
        .end();
      }
    },
    {
      class: 'Boolean',
      name: 'enabled',
      documentation: 'Enables script.',
      tableCellFormatter: function(value) {
        this.start()
          .style({ color: value ? 'green' : 'gray' })
          .add(value ? 'Y' : 'N')
        .end();
      },
      value: true
    },
    {
      class: 'String',
      name: 'description',
      documentation: 'Description of the script.',
      tableCellFormatter: function(value) {
        this.start()
          .style({
            'overflow': 'hidden',
            'max-width': '25ch',
            'min-width': '25ch',
            'text-overflow': 'ellipsis'
          }).add(value)
        .end();
      }
    },
    {
      class: 'DateTime',
      name: 'lastRun',
      documentation: 'Date and time the script ran last.',
      visibility: 'RO'
    },
    {
      class: 'Long',
      name: 'lastDuration',
      documentation: 'Date and time the script took to complete.',
      visibility: 'RO',
      units: 'ms',
      tableCellFormatter: function(value) {
        this.start()
          .style({
            'overflow': 'hidden',
            'max-width': '5ch',
            'min-width': '5ch',
            'text-overflow': 'ellipsis'
          }).add(value)
        .end();
      }
    },
    /*
    {
      class: 'Enum',
      of: 'foam.nanos.script.Language',
      name: 'language',
      value: foam.nanos.script.Language.BEANSHELL,
      transient: true
      // TODO: fix JS support
    },
    */
    {
      class: 'Boolean',
      name: 'server',
      documentation: 'Runs on server side if enabled.',
      value: true
    },
    {
      class: 'foam.core.Enum',
      of: 'foam.nanos.script.ScriptStatus',
      name: 'status',
      documentation: 'Status of script.',
      visibility: 'RO',
      value: 'UNSCHEDULED',
      javaValue: 'ScriptStatus.UNSCHEDULED'
    },
    {
      class: 'String',
      name: 'code',
      view: {
        class: 'foam.u2.tag.TextArea',
        rows: 20, cols: 120,
        css: { 'font-family': 'monospace' }
      }
    },
    {
      class: 'String',
      name: 'output',
      visibility: 'RO',
      view: {
        class: 'foam.u2.tag.TextArea',
        rows: 12, cols: 120,
        css: { 'font-family': 'monospace' }
      },
      preSet: function(_, newVal) {
        // for client side scripts
        if ( newVal.length > this.MAX_OUTPUT_CHARS ) {
          newVal = newVal.substring(0, this.MAX_OUTPUT_CHARS) + '...';
        }
        return newVal;
      },
      javaSetter: `
      // for server side scripts
      if (val.length() > MAX_OUTPUT_CHARS) {
        val = val.substring(0, MAX_OUTPUT_CHARS) + "...";
      }
      output_ = val;
      outputIsSet_ = true;
      `
    },
    {
      class: 'String',
      name: 'notes',
      view: { class: 'foam.u2.tag.TextArea', rows: 4, cols: 144 }
    }
  ],

  methods: [
    {
      name: 'createInterpreter',
      args: [
        { name: 'x', type: 'Context' }
      ],
      javaType: 'Interpreter',
      javaCode: `
        Interpreter shell = new Interpreter();

        try {
          shell.set("currentScript", this);
          shell.set("x", x);
          shell.eval("runScript(String name) { script = x.get(\\"scriptDAO\\").find(name); if ( script != null ) eval(script.code); }");
          shell.eval("foam.core.X sudo(String user) { foam.util.Auth.sudo(x, (String) user); }");
          shell.eval("foam.core.X sudo(Object id) { foam.util.Auth.sudo(x, id); }");
        } catch (EvalError e) {}

        return shell;
      `
    },
    {
      name: 'runScript',
      code: function() {
        var log = function() {
          this.output += Array.from(arguments).join('') + '\n';
        }.bind(this);
        try {
          with ({ log: log, print: log, x: this.__context__ })
          return Promise.resolve(eval(this.code));
        } catch (err) {
          this.output += err;
          return Promise.reject(err);
        }
      },
      args: [
        {
          name: 'x', type: 'Context'
        }
      ],
      javaCode: `
        ByteArrayOutputStream baos  = new ByteArrayOutputStream();
        PrintStream           ps    = new PrintStream(baos);
        Interpreter           shell = createInterpreter(x);
        PM                    pm    = new PM(this.getClass(), getId());

        // TODO: import common packages like foam.core.*, foam.dao.*, etc.
        try {
          setOutput("");
          shell.setOut(ps);
          shell.eval(getCode());
        } catch (Throwable e) {
          ps.println();
          e.printStackTrace(ps);
          e.printStackTrace();
        } finally {
          pm.log(x);
        }

        setLastRun(new Date());
        setLastDuration(pm.getTime());
        ps.flush();
        setOutput(baos.toString());
    `
    },
    {
      name: 'poll',
      code: function() {
        var self = this;
        var interval = setInterval(function() {
            self.scriptDAO.find(self.id).then(function(script) {
              if ( script.status !== self.ScriptStatus.RUNNING ) {
                self.copyFrom(script);
                clearInterval(interval);

                // create notification
                var notification = self.ScriptRunNotification.create({
                  userId: self.user.id,
                  scriptId: script.id,
                  notificationType: 'Script Execution',
                  body: `Status: ${script.status}
                        Script Output: ${script.length > self.MAX_NOTIFICATION_OUTPUT_CHARS ?
                          script.output.substring(0, self.MAX_NOTIFICATION_OUTPUT_CHARS) + '...' :
                          script.output }
                        LastDuration: ${script.lastDuration}`
                });
                self.notificationDAO.put(notification);
              }
            }).catch(function() {
               clearInterval(interval);
              });
        }, 2000);
      }
    }
  ],

  actions: [
    {
      name: 'run',
      code: function() {
        var self = this;
        this.output = '';
        this.status = this.ScriptStatus.SCHEDULED;
        if ( this.server ) {
          this.scriptDAO.put(this).then(function(script) {
              self.copyFrom(script);
              if ( script.status === self.ScriptStatus.RUNNING ) {
                self.poll();
              }
          });
        } else {
          this.status = this.ScriptStatus.RUNNING;
          this.runScript().then(() => {
            this.status = this.ScriptStatus.UNSCHEDULED;
            this.scriptDAO.put(this);
          }).catch((err) => {
            console.log(err);
            this.status = this.ScriptStatus.ERROR;
            this.scriptDAO.put(this);
          });
        }
      }
    }
  ]
});
<commit_msg>Use the ace editor for the nanos Scripts.<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.script',
  name: 'Script',

  implements: ['foam.nanos.auth.EnabledAware'],

  requires: [
    'foam.nanos.script.ScriptStatus',
    'foam.nanos.notification.notifications.ScriptRunNotification'
  ],

  imports: [
    'notificationDAO',
    'scriptDAO',
    'user'
  ],

  javaImports: [
    'bsh.EvalError',
    'bsh.Interpreter',
    'foam.core.*',
    'foam.dao.*',
    'foam.mlang.predicate.Predicate',
    'foam.nanos.auth.*',
    'foam.nanos.pm.PM',
    'foam.nanos.session.Session',
    'java.io.ByteArrayOutputStream',
    'java.io.PrintStream',
    'java.util.Date',
    'java.util.List',
    'static foam.mlang.MLang.*',
  ],

  tableColumns: [
    'id', 'server', 'description', 'lastDuration', 'status', 'run'
  ],

  searchColumns: ['id', 'description'],

  constants: [
    {
      name: 'MAX_OUTPUT_CHARS',
      type: 'Integer',
      value: 20000
    },
    {
      name: 'MAX_NOTIFICATION_OUTPUT_CHARS',
      type: 'Integer',
      value: 200
    }
  ],

  properties: [
    {
      class: 'String',
      name: 'id',
      tableCellFormatter: function(value) {
        this.start()
          .style({
            'overflow': 'hidden',
            'max-width': '25ch',
            'min-width': '25ch',
            'text-overflow': 'ellipsis'
          }).add(value)
        .end();
      }
    },
    {
      class: 'Boolean',
      name: 'enabled',
      documentation: 'Enables script.',
      tableCellFormatter: function(value) {
        this.start()
          .style({ color: value ? 'green' : 'gray' })
          .add(value ? 'Y' : 'N')
        .end();
      },
      value: true
    },
    {
      class: 'String',
      name: 'description',
      documentation: 'Description of the script.',
      tableCellFormatter: function(value) {
        this.start()
          .style({
            'overflow': 'hidden',
            'max-width': '25ch',
            'min-width': '25ch',
            'text-overflow': 'ellipsis'
          }).add(value)
        .end();
      }
    },
    {
      class: 'DateTime',
      name: 'lastRun',
      documentation: 'Date and time the script ran last.',
      visibility: 'RO'
    },
    {
      class: 'Long',
      name: 'lastDuration',
      documentation: 'Date and time the script took to complete.',
      visibility: 'RO',
      units: 'ms',
      tableCellFormatter: function(value) {
        this.start()
          .style({
            'overflow': 'hidden',
            'max-width': '5ch',
            'min-width': '5ch',
            'text-overflow': 'ellipsis'
          }).add(value)
        .end();
      }
    },
    /*
    {
      class: 'Enum',
      of: 'foam.nanos.script.Language',
      name: 'language',
      value: foam.nanos.script.Language.BEANSHELL,
      transient: true
      // TODO: fix JS support
    },
    */
    {
      class: 'Boolean',
      name: 'server',
      documentation: 'Runs on server side if enabled.',
      value: true
    },
    {
      class: 'foam.core.Enum',
      of: 'foam.nanos.script.ScriptStatus',
      name: 'status',
      documentation: 'Status of script.',
      visibility: 'RO',
      value: 'UNSCHEDULED',
      javaValue: 'ScriptStatus.UNSCHEDULED'
    },
    {
      class: 'String',
      name: 'code',
      view: {
        class: 'io.c9.ace.Editor'
      }
    },
    {
      class: 'String',
      name: 'output',
      visibility: 'RO',
      view: {
        class: 'foam.u2.tag.TextArea',
        rows: 12, cols: 120,
        css: { 'font-family': 'monospace' }
      },
      preSet: function(_, newVal) {
        // for client side scripts
        if ( newVal.length > this.MAX_OUTPUT_CHARS ) {
          newVal = newVal.substring(0, this.MAX_OUTPUT_CHARS) + '...';
        }
        return newVal;
      },
      javaSetter: `
      // for server side scripts
      if (val.length() > MAX_OUTPUT_CHARS) {
        val = val.substring(0, MAX_OUTPUT_CHARS) + "...";
      }
      output_ = val;
      outputIsSet_ = true;
      `
    },
    {
      class: 'String',
      name: 'notes',
      view: { class: 'foam.u2.tag.TextArea', rows: 4, cols: 144 }
    }
  ],

  methods: [
    {
      name: 'createInterpreter',
      args: [
        { name: 'x', type: 'Context' }
      ],
      javaType: 'Interpreter',
      javaCode: `
        Interpreter shell = new Interpreter();

        try {
          shell.set("currentScript", this);
          shell.set("x", x);
          shell.eval("runScript(String name) { script = x.get(\\"scriptDAO\\").find(name); if ( script != null ) eval(script.code); }");
          shell.eval("foam.core.X sudo(String user) { foam.util.Auth.sudo(x, (String) user); }");
          shell.eval("foam.core.X sudo(Object id) { foam.util.Auth.sudo(x, id); }");
        } catch (EvalError e) {}

        return shell;
      `
    },
    {
      name: 'runScript',
      code: function() {
        var log = function() {
          this.output += Array.from(arguments).join('') + '\n';
        }.bind(this);
        try {
          with ({ log: log, print: log, x: this.__context__ })
          return Promise.resolve(eval(this.code));
        } catch (err) {
          this.output += err;
          return Promise.reject(err);
        }
      },
      args: [
        {
          name: 'x', type: 'Context'
        }
      ],
      javaCode: `
        ByteArrayOutputStream baos  = new ByteArrayOutputStream();
        PrintStream           ps    = new PrintStream(baos);
        Interpreter           shell = createInterpreter(x);
        PM                    pm    = new PM(this.getClass(), getId());

        // TODO: import common packages like foam.core.*, foam.dao.*, etc.
        try {
          setOutput("");
          shell.setOut(ps);
          shell.eval(getCode());
        } catch (Throwable e) {
          ps.println();
          e.printStackTrace(ps);
          e.printStackTrace();
        } finally {
          pm.log(x);
        }

        setLastRun(new Date());
        setLastDuration(pm.getTime());
        ps.flush();
        setOutput(baos.toString());
    `
    },
    {
      name: 'poll',
      code: function() {
        var self = this;
        var interval = setInterval(function() {
            self.scriptDAO.find(self.id).then(function(script) {
              if ( script.status !== self.ScriptStatus.RUNNING ) {
                self.copyFrom(script);
                clearInterval(interval);

                // create notification
                var notification = self.ScriptRunNotification.create({
                  userId: self.user.id,
                  scriptId: script.id,
                  notificationType: 'Script Execution',
                  body: `Status: ${script.status}
                        Script Output: ${script.length > self.MAX_NOTIFICATION_OUTPUT_CHARS ?
                          script.output.substring(0, self.MAX_NOTIFICATION_OUTPUT_CHARS) + '...' :
                          script.output }
                        LastDuration: ${script.lastDuration}`
                });
                self.notificationDAO.put(notification);
              }
            }).catch(function() {
               clearInterval(interval);
              });
        }, 2000);
      }
    }
  ],

  actions: [
    {
      name: 'run',
      code: function() {
        var self = this;
        this.output = '';
        this.status = this.ScriptStatus.SCHEDULED;
        if ( this.server ) {
          this.scriptDAO.put(this).then(function(script) {
              self.copyFrom(script);
              if ( script.status === self.ScriptStatus.RUNNING ) {
                self.poll();
              }
          });
        } else {
          this.status = this.ScriptStatus.RUNNING;
          this.runScript().then(() => {
            this.status = this.ScriptStatus.UNSCHEDULED;
            this.scriptDAO.put(this);
          }).catch((err) => {
            console.log(err);
            this.status = this.ScriptStatus.ERROR;
            this.scriptDAO.put(this);
          });
        }
      }
    }
  ]
});
<|endoftext|>


<commit_before>import React from 'react'
import Helmet from 'components/Helmet'
import PropTypes from 'prop-types'
import Link from 'components/Link'
import content from './content.json'

export default function Home(props) {
  return (

      <Helmet
        title={content.meta_title}
        description={content.meta_description}
      />

{content.title}

{content.content[0]}

{content.content[1]}

      <Link to='/page-2/'>Go to page 2</Link>

      <Link to='/landing-page/'>Landing Page</Link>

  )
}

Home.propTypes = {
  children: PropTypes.any,
}
<commit_msg>remove proptype<commit_after>import React from 'react'
import Helmet from 'components/Helmet'
import Link from 'components/Link'

import content from 'content/home.json'

export default function Home({ content: home }) {
  return (

      <Helmet
        title={content.meta_title}
        description={content.meta_description}
      />

{content.title}

{content.content[0]}

{content.content[1]}

      <Link to='/page-2/'>Go to page 2</Link>

      <Link to='/landing-page/'>Landing Page</Link>

  )
}
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var utils = require('../../utils');
var gcp = require('../../gcp');
var RSVP = require('rsvp');
var logger = require('../../logger');
var FirebaseError = require('../../error');
var chalk = require('chalk');

module.exports = function(context, options, payload) {
  if (!options.config.has('functions')) {
    return RSVP.resolve();
  }

  var POLL_INTERVAL = 5000; // 5 sec
  var GCP_REGION = 'us-central1';
  var projectId = context.projectId;
  var bucketName = gcp.storage.buckets.name(projectId);
  var sourceUrl = 'gs://' + bucketName + '/' + projectId;

  var functionsInfo = payload.functions.triggers;

  function _fetchTriggerUrls(ops) {
    return gcp.cloudfunctions.list(projectId, GCP_REGION).then(function(functions) {
      var httpFunctions = _.chain(functions).filter({ gcsUrl: sourceUrl}).filter('httpsTrigger').value();
      _.forEach(httpFunctions, function(httpFunc) {
        _.chain(ops).find({ func: httpFunc.name }).assign({ triggerUrl: httpFunc.httpsTrigger.url}).value();
      });
      return RSVP.resolve();
    });
  }

  function _reportResults(successfulCalls, failedCalls) {
    function logFailedOps(ops) {
      _.forEach(ops, function(operation) {
        var parts = operation.func.split('/');
        var functionName = parts[5];
        utils.logWarning(chalk.bold.yellow('functions[' + functionName + ']: ') + 'Deploy Error: ' + operation.error.message);
      });
    }

    function logSuccessfulOps(ops) {
      var functionUrls = [];
      _.forEach(ops, function(operation) {
        var parts = operation.func.split('/');
        var functionName = parts[5];
        if (operation.triggerUrl && operation.type !== 'delete') {
          var entry = { 'funcName': functionName, 'url': operation.triggerUrl};
          functionUrls = _.concat(functionUrls, entry);
        }
        utils.logSuccess(chalk.bold.green('functions[' + functionName + ']: ') + 'Successful ' + operation.type + ' operation. ');
      });

      if (functionUrls.length > 0) {
        _.set(context, 'functionUrls', functionUrls);
      }
    }

    var failedChecks = _.filter(successfulCalls, function(op) {
      return !op.done && op.error;
    });
    var failedOps = _.filter(successfulCalls, function(op) {
      return op.done && op.error;
    });
    var successfulOps = _.filter(successfulCalls, function(op) {
      return op.done && !op.error;
    });

    var failed = false;
    if (failedCalls.length > 0) {
      failed = true;
      utils.logWarning(chalk.bold.yellow('functions: ') + failedCalls.length + ' function(s) failed to be deployed.');
    }
    if (failedOps.length > 0) {
      failed = true;
      logFailedOps(failedOps);
    }
    if (failedChecks.length > 0) {
      utils.logWarning(chalk.bold.yellow('functions: ') + 'failed to get status of ' + failedChecks.length + ' operation(s).');
      utils.logWarning(chalk.bold.yellow('functions: ') + 'run ' + chalk.bold('firebase functions:log') + ' in a few minutes to ensure that your functions deployed properly.');
    }
    if (successfulOps.length > 0) {
      logSuccessfulOps(successfulOps);
      if (!failed) {
        utils.logSuccess(chalk.bold.green('functions: ') + 'all functions deployed successfully!');
      } else {
        utils.logSuccess(chalk.bold.green('functions: ') + successfulOps.length + ' function(s) deployed successfully.');
      }
    }
    return failed ? RSVP.reject() : RSVP.resolve();
  }

  function _pollOperations(successfulCalls) {
    function logOps(ops) {
      _.forEach(ops, function(operation) {
        var parts = operation.func.split('/');
        var functionName = parts[5];
        var msg = '[functions] operation poll: '
          + functionName + ': '
          + operation.type + ' ' + operation.name + ' is '
          + (operation.done ? 'done.' : 'not done.');
        logger.debug(msg);
      });
    }
    return new RSVP.Promise(function(resolve, reject) {
      function poll() {
        var unfinishedOps = _.chain(successfulCalls).filter(function(operation) {
          return !(operation.done || operation.error);
        }).map(function(operation) {
          return gcp.cloudfunctions.check(operation);
        }).value();

        if (unfinishedOps.length === 0) {
          return resolve();
        }

        RSVP.allSettled(unfinishedOps).then(function(checks) {
          var successfulChecks = _.chain(checks).filter({'state': 'fulfilled'}).map('value').value();
          logOps(successfulChecks);

          var allDone = _.every(successfulChecks, { done: true });
          if (allDone) {
            return resolve();
          }
          setTimeout(poll, POLL_INTERVAL);
        }).catch(function(err) {
          return reject(err);
        });
      }
      poll();
    });
  }

  function _prepFunctionOp(functionInfo) {
    if (functionInfo.httpsTrigger) {
      return RSVP.resolve(_.pick(functionInfo, 'httpsTrigger'));
    } else if (functionInfo.eventTrigger) {
      var trigger = functionInfo.eventTrigger;
      if (trigger.eventType === 'providers/cloud.storage/eventTypes/object.change' && !trigger.resource) {
        var bucket = _.get(context, 'firebaseConfig.storageBucket');
        _.set(trigger, 'resource', 'projects/_/buckets/' + bucket);
      }
      return RSVP.resolve({eventTrigger: trigger});
    }
    return RSVP.reject(new Error('Could not parse function trigger, unknown trigger type.'));
  }

  delete payload.functions;
  var uploadedNames = _.map(functionsInfo, 'name');

  return gcp.cloudfunctions.list(projectId, GCP_REGION).then(function(existingFunctions) {
    var pluckName = function(functionObject) {
      var fullName = _.get(functionObject, 'name'); // e.g.'projects/proj1/locations/us-central1/functions/func'
      return _.last(fullName.split('/'));
    };

    var existingNames = _.map(existingFunctions, pluckName);

    var addOps = _.chain(uploadedNames)
      .difference(existingNames)
      .map(function(functionName) {
        var functionInfo = _.find(functionsInfo, {'name': functionName});
        return _prepFunctionOp(functionInfo).then(function(functionTrigger) {
          utils.logBullet(chalk.bold.cyan('functions: ') + 'creating function ' + chalk.bold(functionName) + '...');
          return gcp.cloudfunctions.create(projectId, GCP_REGION, functionName, functionInfo.entryPoint, functionTrigger, sourceUrl);
        });
      }).value();

    var updateOps = _.chain(uploadedNames)
      .intersection(existingNames)
      .map(function(functionName) {
        var functionInfo = _.find(functionsInfo, {'name': functionName});
        return _prepFunctionOp(functionInfo, functionName).then(function(functionTrigger) {
          utils.logBullet(chalk.bold.cyan('functions: ') + 'updating function ' + chalk.bold(functionName) + '...');
          var existingFunction = _.find(existingFunctions, function(func) {
            return func.name.match(new RegExp('/' + functionName + '$'));
          });
          var existingTriggerType = _.get(existingFunction, 'eventTrigger.eventType');
          var newTriggerType = _.get(functionTrigger, 'eventTrigger.eventType');
          var isNowDBTrigger = newTriggerType === 'providers/firebase.database/eventTypes/data.write';
          // Firebase Database triggers used to be an odd prototype that was really a pubsub trigger. GCF won't allow
          // us to change the trigger type of an existing trigger so we have to delete functions that were based on
          // the old prototype and create them as real database triggers.
          if (isNowDBTrigger && newTriggerType !== existingTriggerType) {
            return gcp.cloudfunctions.delete(projectId, GCP_REGION, functionName)
              .then(function(call) {
                return _pollOperations([call]);
              }).then(function() {
                return gcp.cloudfunctions.create(
                  projectId,
                  GCP_REGION,
                  functionName,
                  functionInfo.entryPoint,
                  functionTrigger, sourceUrl);
              });
          }
          return gcp.cloudfunctions.update(projectId, GCP_REGION, functionName, functionInfo.entryPoint, functionTrigger, sourceUrl);
        });
      }).value();

    var deleteOps = _.chain(existingFunctions)
      .filter({ sourceArchiveUrl: sourceUrl }) // only delete functions uploaded via firebase-tools
      .map(pluckName)
      .difference(uploadedNames)
      .map(function(functionName) {
        utils.logBullet(chalk.bold.cyan('functions: ') + 'deleting function ' + chalk.bold(functionName) + '...');
        return gcp.cloudfunctions.delete(projectId, GCP_REGION, functionName);
      }).value();

    return RSVP.allSettled([].concat(addOps, updateOps, deleteOps));
  }).then(function(allOps) {
    var failedCalls = _.chain(allOps).filter({'state': 'rejected'}).map('reason').value();
    var successfulCalls = _.chain(allOps).filter({'state': 'fulfilled'}).map('value').value();
    var fetch =  _fetchTriggerUrls(successfulCalls);
    var poll = _pollOperations(successfulCalls);

    return RSVP.allSettled([fetch, poll]).then(function() {
      return _reportResults(successfulCalls, failedCalls);
    });
  }).catch(function() {
    logger.info('\n\nFunctions deploy had errors. To continue deploying other features (such as database), run:');
    logger.info('    ' + chalk.bold('firebase deploy --except functions'));
    return RSVP.reject(new FirebaseError('Functions did not deploy properly.'));
  });
};
<commit_msg>fix bug where http function urls weren't appearing after deploys (#148)<commit_after>'use strict';

var _ = require('lodash');
var utils = require('../../utils');
var gcp = require('../../gcp');
var RSVP = require('rsvp');
var logger = require('../../logger');
var FirebaseError = require('../../error');
var chalk = require('chalk');

module.exports = function(context, options, payload) {
  if (!options.config.has('functions')) {
    return RSVP.resolve();
  }

  var POLL_INTERVAL = 5000; // 5 sec
  var GCP_REGION = 'us-central1';
  var projectId = context.projectId;
  var bucketName = gcp.storage.buckets.name(projectId);
  var sourceUrl = 'gs://' + bucketName + '/' + projectId;

  var functionsInfo = payload.functions.triggers;

  function _fetchTriggerUrls(ops) {
    return gcp.cloudfunctions.list(projectId, GCP_REGION).then(function(functions) {
      var httpFunctions = _.chain(functions).filter({ sourceArchiveUrl: sourceUrl}).filter('httpsTrigger').value();
      _.forEach(httpFunctions, function(httpFunc) {
        _.chain(ops).find({ func: httpFunc.name }).assign({ triggerUrl: httpFunc.httpsTrigger.url}).value();
      });
      return RSVP.resolve();
    });
  }

  function _reportResults(successfulCalls, failedCalls) {
    function logFailedOps(ops) {
      _.forEach(ops, function(operation) {
        var parts = operation.func.split('/');
        var functionName = parts[5];
        utils.logWarning(chalk.bold.yellow('functions[' + functionName + ']: ') + 'Deploy Error: ' + operation.error.message);
      });
    }

    function logSuccessfulOps(ops) {
      var functionUrls = [];
      _.forEach(ops, function(operation) {
        var parts = operation.func.split('/');
        var functionName = parts[5];
        if (operation.triggerUrl && operation.type !== 'delete') {
          var entry = { 'funcName': functionName, 'url': operation.triggerUrl};
          functionUrls = _.concat(functionUrls, entry);
        }
        utils.logSuccess(chalk.bold.green('functions[' + functionName + ']: ') + 'Successful ' + operation.type + ' operation. ');
      });

      if (functionUrls.length > 0) {
        _.set(context, 'functionUrls', functionUrls);
      }
    }

    var failedChecks = _.filter(successfulCalls, function(op) {
      return !op.done && op.error;
    });
    var failedOps = _.filter(successfulCalls, function(op) {
      return op.done && op.error;
    });
    var successfulOps = _.filter(successfulCalls, function(op) {
      return op.done && !op.error;
    });

    var failed = false;
    if (failedCalls.length > 0) {
      failed = true;
      utils.logWarning(chalk.bold.yellow('functions: ') + failedCalls.length + ' function(s) failed to be deployed.');
    }
    if (failedOps.length > 0) {
      failed = true;
      logFailedOps(failedOps);
    }
    if (failedChecks.length > 0) {
      utils.logWarning(chalk.bold.yellow('functions: ') + 'failed to get status of ' + failedChecks.length + ' operation(s).');
      utils.logWarning(chalk.bold.yellow('functions: ') + 'run ' + chalk.bold('firebase functions:log') + ' in a few minutes to ensure that your functions deployed properly.');
    }
    if (successfulOps.length > 0) {
      logSuccessfulOps(successfulOps);
      if (!failed) {
        utils.logSuccess(chalk.bold.green('functions: ') + 'all functions deployed successfully!');
      } else {
        utils.logSuccess(chalk.bold.green('functions: ') + successfulOps.length + ' function(s) deployed successfully.');
      }
    }
    return failed ? RSVP.reject() : RSVP.resolve();
  }

  function _pollOperations(successfulCalls) {
    function logOps(ops) {
      _.forEach(ops, function(operation) {
        var parts = operation.func.split('/');
        var functionName = parts[5];
        var msg = '[functions] operation poll: '
          + functionName + ': '
          + operation.type + ' ' + operation.name + ' is '
          + (operation.done ? 'done.' : 'not done.');
        logger.debug(msg);
      });
    }
    return new RSVP.Promise(function(resolve, reject) {
      function poll() {
        var unfinishedOps = _.chain(successfulCalls).filter(function(operation) {
          return !(operation.done || operation.error);
        }).map(function(operation) {
          return gcp.cloudfunctions.check(operation);
        }).value();

        if (unfinishedOps.length === 0) {
          return resolve();
        }

        RSVP.allSettled(unfinishedOps).then(function(checks) {
          var successfulChecks = _.chain(checks).filter({'state': 'fulfilled'}).map('value').value();
          logOps(successfulChecks);

          var allDone = _.every(successfulChecks, { done: true });
          if (allDone) {
            return resolve();
          }
          setTimeout(poll, POLL_INTERVAL);
        }).catch(function(err) {
          return reject(err);
        });
      }
      poll();
    });
  }

  function _prepFunctionOp(functionInfo) {
    if (functionInfo.httpsTrigger) {
      return RSVP.resolve(_.pick(functionInfo, 'httpsTrigger'));
    } else if (functionInfo.eventTrigger) {
      var trigger = functionInfo.eventTrigger;
      if (trigger.eventType === 'providers/cloud.storage/eventTypes/object.change' && !trigger.resource) {
        var bucket = _.get(context, 'firebaseConfig.storageBucket');
        _.set(trigger, 'resource', 'projects/_/buckets/' + bucket);
      }
      return RSVP.resolve({eventTrigger: trigger});
    }
    return RSVP.reject(new Error('Could not parse function trigger, unknown trigger type.'));
  }

  delete payload.functions;
  var uploadedNames = _.map(functionsInfo, 'name');

  return gcp.cloudfunctions.list(projectId, GCP_REGION).then(function(existingFunctions) {
    var pluckName = function(functionObject) {
      var fullName = _.get(functionObject, 'name'); // e.g.'projects/proj1/locations/us-central1/functions/func'
      return _.last(fullName.split('/'));
    };

    var existingNames = _.map(existingFunctions, pluckName);

    var addOps = _.chain(uploadedNames)
      .difference(existingNames)
      .map(function(functionName) {
        var functionInfo = _.find(functionsInfo, {'name': functionName});
        return _prepFunctionOp(functionInfo).then(function(functionTrigger) {
          utils.logBullet(chalk.bold.cyan('functions: ') + 'creating function ' + chalk.bold(functionName) + '...');
          return gcp.cloudfunctions.create(projectId, GCP_REGION, functionName, functionInfo.entryPoint, functionTrigger, sourceUrl);
        });
      }).value();

    var updateOps = _.chain(uploadedNames)
      .intersection(existingNames)
      .map(function(functionName) {
        var functionInfo = _.find(functionsInfo, {'name': functionName});
        return _prepFunctionOp(functionInfo, functionName).then(function(functionTrigger) {
          utils.logBullet(chalk.bold.cyan('functions: ') + 'updating function ' + chalk.bold(functionName) + '...');
          var existingFunction = _.find(existingFunctions, function(func) {
            return func.name.match(new RegExp('/' + functionName + '$'));
          });
          var existingTriggerType = _.get(existingFunction, 'eventTrigger.eventType');
          var newTriggerType = _.get(functionTrigger, 'eventTrigger.eventType');
          var isNowDBTrigger = newTriggerType === 'providers/firebase.database/eventTypes/data.write';
          // Firebase Database triggers used to be an odd prototype that was really a pubsub trigger. GCF won't allow
          // us to change the trigger type of an existing trigger so we have to delete functions that were based on
          // the old prototype and create them as real database triggers.
          if (isNowDBTrigger && newTriggerType !== existingTriggerType) {
            return gcp.cloudfunctions.delete(projectId, GCP_REGION, functionName)
              .then(function(call) {
                return _pollOperations([call]);
              }).then(function() {
                return gcp.cloudfunctions.create(
                  projectId,
                  GCP_REGION,
                  functionName,
                  functionInfo.entryPoint,
                  functionTrigger, sourceUrl);
              });
          }
          return gcp.cloudfunctions.update(projectId, GCP_REGION, functionName, functionInfo.entryPoint, functionTrigger, sourceUrl);
        });
      }).value();

    var deleteOps = _.chain(existingFunctions)
      .filter({ sourceArchiveUrl: sourceUrl }) // only delete functions uploaded via firebase-tools
      .map(pluckName)
      .difference(uploadedNames)
      .map(function(functionName) {
        utils.logBullet(chalk.bold.cyan('functions: ') + 'deleting function ' + chalk.bold(functionName) + '...');
        return gcp.cloudfunctions.delete(projectId, GCP_REGION, functionName);
      }).value();

    return RSVP.allSettled([].concat(addOps, updateOps, deleteOps));
  }).then(function(allOps) {
    var failedCalls = _.chain(allOps).filter({'state': 'rejected'}).map('reason').value();
    var successfulCalls = _.chain(allOps).filter({'state': 'fulfilled'}).map('value').value();
    var fetch =  _fetchTriggerUrls(successfulCalls);
    var poll = _pollOperations(successfulCalls);

    return RSVP.allSettled([fetch, poll]).then(function() {
      return _reportResults(successfulCalls, failedCalls);
    });
  }).catch(function() {
    logger.info('\n\nFunctions deploy had errors. To continue deploying other features (such as database), run:');
    logger.info('    ' + chalk.bold('firebase deploy --except functions'));
    return RSVP.reject(new FirebaseError('Functions did not deploy properly.'));
  });
};
<|endoftext|>


<commit_before>window.__config = {
	env: 'dev'
};

import ArticlePreview from '../components/ArticlePreview';
import Page from '../components/Page';
import util from '../components/Util';
import Article from '../components/Article';
import AboutPage from '../components/AboutPage';
import ContactPage from '../components/ContactPage'
import CommonNode from '../components/basic/CommonNode';

require('../app/less/common.less');
require('../app/less/main.less');

let { Router, Route, Link, hashHistory, IndexRoute} = ReactRouter;

class ArticlePreviewPage extends CommonNode {
	constructor(props){
		super(props);
		this.state= {
			articles: [],
			hasMoreData: true
		};
		this.currentPage = 1;
		this.pageSize = 5;
	}

	componentDidMount(){
		let me = this;
		me.fetchData(me.currentPage);

		let lastScrollTop = 0;
		me.handleFetchData = util.throttle(function (e){
			var st = window.pageYOffset || document.documentElement.scrollTop;

			//鼠标滚轮向下滑动 且 到达底部
			if (st > lastScrollTop
				&& me.state.hasMoreData
				&& $(window).scrollTop() + $(window).height() > $('.drip-ui-footer').offset().top) {

				me.fetchData( ++me.currentPage );
			}
			lastScrollTop = st;

		}, 100);

		ReactDOM.findDOMNode(me).addEventListener('scroll',me.handleFetchData , false);
	}

	componentWillUnmount(){
		ReactDOM.findDOMNode(this).removeEventListener('scroll', this.handleFetchData, false);
	}

	fetchData(currentPage){
		let me = this;
		$.ajax({
			url: '/api/article',
			method: 'GET',
			data: {currentPage: currentPage, pageSize: me.pageSize},
			success: function(res){
				if(res.articles && res.articles.length){
					me.loading.stop();
					me.setState({
						articles: me.state.articles.concat(res.articles),
						hasMoreData: res.currentPage < res.pageCount
					});
				}
			}
		});
	}

	render(){

		return (
			<ArticlePreview articles={this.state.articles} hasMoreData={this.state.hasMoreData}/>
			);
	}
}

class ArticlePage extends React.Component {
	constructor(props){
		super(props);
	}

	render(){
		return (<Article />);
	}
}

ReactDOM.render((
	<Router history={hashHistory}>
		<Route path="/" component={Page}>
			<IndexRoute component={ArticlePreviewPage}/>
			<Route path="about" component={AboutPage}/>
			<Route path="contact" component={ContactPage}/>
			<Route path="article" component={ArticlePreviewPage}/>
			<Route path="article/:id" component={ArticlePage}/>
		</Route>

	</Router>
) , document.getElementById('app'));

<commit_msg>fix defect.<commit_after>window.__config = {
	env: 'dev'
};

import ArticlePreview from '../components/ArticlePreview';
import Page from '../components/Page';
import util from '../components/Util';
import Article from '../components/Article';
import AboutPage from '../components/AboutPage';
import ContactPage from '../components/ContactPage'
import CommonNode from '../components/basic/CommonNode';

require('../app/less/common.less');
require('../app/less/main.less');

let { Router, Route, Link, hashHistory, IndexRoute} = ReactRouter;

class ArticlePreviewPage extends CommonNode {
	constructor(props){
		super(props);
		this.state= {
			articles: [],
			hasMoreData: true
		};
		this.currentPage = 1;
		this.pageSize = 5;
	}

	componentDidMount(){
		let me = this;
		me.fetchData(me.currentPage);

		let lastScrollTop = 0;
		me.handleFetchData = util.throttle(function (e){
			var st = window.pageYOffset || document.documentElement.scrollTop;

			//鼠标滚轮向下滑动 且 到达底部
			if (st > lastScrollTop
				&& me.state.hasMoreData
				&& $(window).scrollTop() + $(window).height() > $('.drip-ui-footer').offset().top) {

				me.fetchData( ++me.currentPage );
			}
			lastScrollTop = st;

		}, 100);

		window.addEventListener('scroll',me.handleFetchData , false);
	}

	componentWillUnmount(){
		ReactDOM.findDOMNode(this).removeEventListener('scroll', this.handleFetchData, false);
	}

	fetchData(currentPage){
		let me = this;
		$.ajax({
			url: '/api/article',
			method: 'GET',
			data: {currentPage: currentPage, pageSize: me.pageSize},
			success: function(res){
				if(res.articles && res.articles.length){
					me.loading.stop();
					me.setState({
						articles: me.state.articles.concat(res.articles),
						hasMoreData: res.currentPage < res.pageCount
					});
				}
			}
		});
	}

	render(){

		return (
			<ArticlePreview articles={this.state.articles} hasMoreData={this.state.hasMoreData}/>
			);
	}
}

class ArticlePage extends React.Component {
	constructor(props){
		super(props);
	}

	render(){
		return (<Article />);
	}
}

ReactDOM.render((
	<Router history={hashHistory}>
		<Route path="/" component={Page}>
			<IndexRoute component={ArticlePreviewPage}/>
			<Route path="about" component={AboutPage}/>
			<Route path="contact" component={ContactPage}/>
			<Route path="article" component={ArticlePreviewPage}/>
			<Route path="article/:id" component={ArticlePage}/>
		</Route>

	</Router>
) , document.getElementById('app'));

<|endoftext|>


<commit_before>import React from "react";
import styled from "styled-components";
import Img from "gatsby-image";
import { graphql } from "gatsby";
import Center from "../components/center";
import ResponsiveContainer from "../components/responsive-container";
import Inset from "../components/inset";
import Text from "../components/text";
import colors from "../constants/colors";
import Layout from "../layouts";
import VersandhausWalzLogo from "../assets/compressed/versandhaus-walz.png";
import OttoLogo from "../assets/compressed/otto.png";
import LudwigMediaLogo from "../assets/compressed/ludwigmedia.png";
import BadischerWeinLogo from "../assets/compressed/badischer-wein.png";

const customers = [
  {
    src: OttoLogo,
    alt: "Otto (GmbH & CoKG)"
  },
  {
    src: VersandhausWalzLogo,
    alt: "Versandhaus Walz"
  },
  {
    src: LudwigMediaLogo,
    alt: "LUDWIG:media gmbh"
  },
  {
    src: BadischerWeinLogo,
    alt: "Badischer Wein eKfr"
  }
];

const HeroImage = styled.div`
  height: 80vh;
  position: relative;
`;

const HeroDarkener = Center.extend`
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background-color: rgba(0, 0, 0, 0.8);
  flex-grow: 1;
`;

const HeroText = styled.h1`
  line-height: 1.1;
  font-size: 2.5rem;
  max-width: 22ch;
  @media (min-width: 50em) {
    font-size: 3rem;
  }
  text-align: center;
  color: ${colors.white};
`;

const HeroTextHighlicht = styled.span`
  color: ${colors.orange};
`;

const CustomerContainer = ResponsiveContainer.extend`
  display: flex;
  align-items: center;
  flex-direction: column;
  @media (min-width: 700px) {
    flex-direction: row;
  }
`;

const CustomerList = Center.extend`
  padding: 32px 0;
  background-color: hsla(13, 5%, 90%, 1);
`;

const CustomerLogo = styled.div`
  flex: 1 1 auto;
  max-height: 60px;
  margin: 10px;
  transition: all 0.5s ease-in-out;

  > img {
    max-width: 100%;
    max-height: 100%;
  }
`;

const H2 = styled.h2`
  color: ${colors.textPrimary};
  margin: 0 0 24px;
`;
const H3 = styled.h3`
  color: ${colors.textPrimary};
  margin: 0 0 16px;
`;

const SkillGrid = styled.div`
  display: grid;
  grid-template-columns: 1fr;
  grid-gap: 16px;
  @media (min-width: 700px) {
    grid-template-columns: 1fr 1fr;
  }
`;

const ServiceSection = styled.div`
  background-color: hsla(13, 10%, 97%, 1);
  padding: 32px 0;
`;

const Skill = styled.div`
  background-color: white;
  border-radius: 4px;

  display: flex;
  flex-direction: column;

  text-align: left;
  color: ${colors.text};
  animation: fadein 2s;
`;

const SkillImage = styled.div`
  max-height: 175px;
  background-color: #ffe6df;
  border-radius: 4px 4px 0 0;
  overflow: hidden;
`;

const IndexPage = ({ data }) => (
  <Layout>
    <HeroImage>

      <HeroDarkener>
        <HeroText>
          Wir liefern digitale Lösungen,{" "}
          <HeroTextHighlicht>die unsere Kunden lieben.</HeroTextHighlicht>
        </HeroText>
      </HeroDarkener>
    </HeroImage>
    <CustomerList>
      <CustomerContainer>
        {customers.map(logo => (
          <CustomerLogo key={logo.alt}>

          </CustomerLogo>
        ))}
      </CustomerContainer>
    </CustomerList>
    <ServiceSection>
      <ResponsiveContainer>

Unsere Leistungen

        <SkillGrid>
          {data.allContentfulServices.edges.map(({ node }) => (
            <Skill key={node.id}>
              <SkillImage>
                {node.image && }
              </SkillImage>
              <Inset scale="xl">

{node.title}

                <Text.Detail>

                </Text.Detail>
              </Inset>
            </Skill>
          ))}
        </SkillGrid>
      </ResponsiveContainer>
    </ServiceSection>
  </Layout>
);

export default IndexPage;

export const query = graphql`
  query AllServices {
    allContentfulServices(sort: { fields: [order], order: ASC }) {
      edges {
        node {
          id
          title
          description {
            childMarkdownRemark {
              html
            }
          }
          image {
            fluid(maxWidth: 426) {
              ...GatsbyContentfulFluid
            }
          }
        }
      }
    }
    contentfulAsset(title: { eq: "Hero" }) {
      fluid(maxHeight: 1000) {
        ...GatsbyContentfulFluid
      }
    }
  }
`;
<commit_msg>feat(Hero): add subheadline<commit_after>import React from "react";
import styled from "styled-components";
import Img from "gatsby-image";
import { graphql } from "gatsby";
import Center from "../components/center";
import ResponsiveContainer from "../components/responsive-container";
import Inset from "../components/inset";
import Text from "../components/text";
import colors from "../constants/colors";
import Layout from "../layouts";
import VersandhausWalzLogo from "../assets/compressed/versandhaus-walz.png";
import OttoLogo from "../assets/compressed/otto.png";
import LudwigMediaLogo from "../assets/compressed/ludwigmedia.png";
import BadischerWeinLogo from "../assets/compressed/badischer-wein.png";

const customers = [
  {
    src: OttoLogo,
    alt: "Otto (GmbH & CoKG)"
  },
  {
    src: VersandhausWalzLogo,
    alt: "Versandhaus Walz"
  },
  {
    src: LudwigMediaLogo,
    alt: "LUDWIG:media gmbh"
  },
  {
    src: BadischerWeinLogo,
    alt: "Badischer Wein eKfr"
  }
];

const HeroImage = styled.div`
  height: 80vh;
  position: relative;
`;

const HeroDarkener = Center.extend`
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  background-color: rgba(0, 0, 0, 0.8);
  flex-grow: 1;
  flex-direction: column;
`;

const HeroText = styled.h1`
  line-height: 1.1;
  font-size: 2.5rem;
  max-width: 22ch;
  @media (min-width: 50em) {
    font-size: 3rem;
  }
  text-align: center;
  color: ${colors.white};
`;

const HeroTextHighlicht = styled.span`
  color: ${colors.orange};
`;

const HeroTextSubheadline = styled.p`
  line-height: 1.1;
  font-size: 2.0rem;
  max-width: 22ch;
  text-align: center;
  color: #ddb992;
  margin: 0;
`;

const CustomerContainer = ResponsiveContainer.extend`
  display: flex;
  align-items: center;
  flex-direction: column;
  @media (min-width: 700px) {
    flex-direction: row;
  }
`;

const CustomerList = Center.extend`
  padding: 32px 0;
  background-color: hsla(13, 5%, 90%, 1);
`;

const CustomerLogo = styled.div`
  flex: 1 1 auto;
  max-height: 60px;
  margin: 10px;
  transition: all 0.5s ease-in-out;

  > img {
    max-width: 100%;
    max-height: 100%;
  }
`;

const H2 = styled.h2`
  color: ${colors.textPrimary};
  margin: 0 0 24px;
`;
const H3 = styled.h3`
  color: ${colors.textPrimary};
  margin: 0 0 16px;
`;

const SkillGrid = styled.div`
  display: grid;
  grid-template-columns: 1fr;
  grid-gap: 16px;
  @media (min-width: 700px) {
    grid-template-columns: 1fr 1fr;
  }
`;

const ServiceSection = styled.div`
  background-color: hsla(13, 10%, 97%, 1);
  padding: 32px 0;
`;

const Skill = styled.div`
  background-color: white;
  border-radius: 4px;

  display: flex;
  flex-direction: column;

  text-align: left;
  color: ${colors.text};
  animation: fadein 2s;
`;

const SkillImage = styled.div`
  max-height: 175px;
  background-color: #ffe6df;
  border-radius: 4px 4px 0 0;
  overflow: hidden;
`;

const IndexPage = ({ data }) => (
  <Layout>
    <HeroImage>

      <HeroDarkener>
        <HeroText>
          Wir liefern digitale Lösungen,{" "}
          <HeroTextHighlicht>die unsere Kunden lieben.</HeroTextHighlicht>
        </HeroText>
        <HeroTextSubheadline>Und das seit 15 Jahren!</HeroTextSubheadline>
      </HeroDarkener>
    </HeroImage>
    <CustomerList>
      <CustomerContainer>
        {customers.map(logo => (
          <CustomerLogo key={logo.alt}>

          </CustomerLogo>
        ))}
      </CustomerContainer>
    </CustomerList>
    <ServiceSection>
      <ResponsiveContainer>

Unsere Leistungen

        <SkillGrid>
          {data.allContentfulServices.edges.map(({ node }) => (
            <Skill key={node.id}>
              <SkillImage>
                {node.image && }
              </SkillImage>
              <Inset scale="xl">

{node.title}

                <Text.Detail>

                </Text.Detail>
              </Inset>
            </Skill>
          ))}
        </SkillGrid>
      </ResponsiveContainer>
    </ServiceSection>
  </Layout>
);

export default IndexPage;

export const query = graphql`
  query AllServices {
    allContentfulServices(sort: { fields: [order], order: ASC }) {
      edges {
        node {
          id
          title
          description {
            childMarkdownRemark {
              html
            }
          }
          image {
            fluid(maxWidth: 426) {
              ...GatsbyContentfulFluid
            }
          }
        }
      }
    }
    contentfulAsset(title: { eq: "Hero" }) {
      fluid(maxHeight: 1000) {
        ...GatsbyContentfulFluid
      }
    }
  }
`;
<|endoftext|>


<commit_before>import React from "react"
import Link from "gatsby-link"
import get from "lodash/get"
import Helmet from "react-helmet"
import include from "underscore.string/include"
import moment from 'moment'
import sortBy from 'lodash/sortBy'
import groupBy from 'lodash/groupBy'

import Bio from "../components/Bio"
import { rhythm } from "../utils/typography"

class BlogIndex extends React.Component {
    //   const sortedPages = sortBy(this.props.route.pages, page => access(page, 'data.date')).reverse()
    // sortedPages.forEach((page) => {
    //   if (access(page, 'file.ext') === 'md' && access(page, 'data.layout') === 'post') {
    //     const title = access(page, 'data.title') || page.path
    //     const description = access(page, 'data.description')
    //     const datePublished = access(page, 'data.date')
    //     const category = access(page, 'data.category')
    //     const image = access(page, 'data.indexImage')

    //     pageLinks.push((
    //

    //         <time dateTime={moment(datePublished).format('MMMM D, YYYY')}>
    //           {moment(datePublished).format('MMMM YYYY')}
    //         </time>
    //
    //         {category}
    //
<Link style={{ borderBottom: 'none' }} to={prefixLink(page.path)}>{title}</Link>

    //
    //

    //         <Link className='readmore' to={prefixLink(page.path)}>Read</Link>
    //

    //     ))
    //   }
    // })

  render () {
    // console.log("props", this.props)
    const siteTitle = get(this, "props.data.site.siteMetadata.title")
    const posts = get(this, "props.data.allMarkdownRemark.edges")
    const sortedPosts = sortBy(posts, post => get(post, "node.frontmatter.date")).reverse()
            const pageLinks = sortedPosts.map(post => {
              if (post.node.path !== "/404/") {
                const title = get(post, "node.frontmatter.title") || post.node.path
                const description = get(post, 'node.frontmatter.description')
                const datePublished = get(post, 'node.frontmatter.date')
                const category = get(post, 'node.frontmatter.category')
                const image = get(post, 'node.frontmatter.indexImage')
                const mobile = window.screen.width < 720
                const fontSize = mobile ? '3.5vw' : null
                console.log(image)
                return (
                  <Link style={{ boxShadow: 'none' }} to={post.node.fields.slug}>

{title}

{moment(datePublished).fromNow()}

                  </Link>
                )
              }
            })
    return (

        <Helmet title={get(this, "props.data.site.siteMetadata.title")} />

          {pageLinks}

    )
  }
}

BlogIndex.propTypes = {
  route: React.PropTypes.object,
}

export default BlogIndex

export const pageQuery = graphql`
query IndexQuery {
  site {
    siteMetadata {
      title
    }
  }
  allMarkdownRemark {
    edges {
      node {
        fields {
          slug
        }
        frontmatter {
          title,
          date,
          description,
          category,
          indexImage
        }
      }
    }
  }
}
`
<commit_msg>set window safely<commit_after>import React from "react"
import Link from "gatsby-link"
import get from "lodash/get"
import Helmet from "react-helmet"
import include from "underscore.string/include"
import moment from 'moment'
import sortBy from 'lodash/sortBy'
import groupBy from 'lodash/groupBy'

import Bio from "../components/Bio"
import { rhythm } from "../utils/typography"

class BlogIndex extends React.Component {
  constructor () {
    super()
    this.state = {
      mobile: false
    }
  }
  componentDidMount () {
    if (typeof (window) !== 'undefined') {
      this.setState({mobile: window.screen.width < 720})
    }
  }

  render () {
    // console.log("props", this.props)
    const siteTitle = get(this, "props.data.site.siteMetadata.title")
    const posts = get(this, "props.data.allMarkdownRemark.edges")
    const sortedPosts = sortBy(posts, post => get(post, "node.frontmatter.date")).reverse()
            const pageLinks = sortedPosts.map(post => {
              if (post.node.path !== "/404/") {
                const title = get(post, "node.frontmatter.title") || post.node.path
                const description = get(post, 'node.frontmatter.description')
                const datePublished = get(post, 'node.frontmatter.date')
                const category = get(post, 'node.frontmatter.category')
                const image = get(post, 'node.frontmatter.indexImage')
                const fontSize = this.state.mobile ? '3.5vw' : null
                return (
                  <Link style={{ boxShadow: 'none' }} to={post.node.fields.slug}>

{title}

{moment(datePublished).fromNow()}

                  </Link>
                )
              }
            })
    return (

        <Helmet title={get(this, "props.data.site.siteMetadata.title")} />

          {pageLinks}

    )
  }
}

BlogIndex.propTypes = {
  route: React.PropTypes.object,
}

export default BlogIndex

export const pageQuery = graphql`
query IndexQuery {
  site {
    siteMetadata {
      title
    }
  }
  allMarkdownRemark {
    edges {
      node {
        fields {
          slug
        }
        frontmatter {
          title,
          date,
          description,
          category,
          indexImage
        }
      }
    }
  }
}
`
<|endoftext|>


<commit_before>import React, { Fragment } from 'react'
import {
  BackgroundImage,
  Box,
  Container,
  Flex,
  Heading,
  Link as A,
  Section,
  Text
} from '@hackclub/design-system'
import styled, { css } from 'styled-components'
import Helmet from 'react-helmet'
import Link from 'gatsby-link'
import Nav from 'components/Nav'
import Module from 'components/Module'
import Framed from 'components/Framed'
import Start from 'components/Start'
import Footer from 'components/Footer'

const shadows = `
  h1,
  h2 {
    color: #fff;
    text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.75);
  }
`
const PhotoSection = styled(Section)`
  position: relative;
  background: linear-gradient(rgba(0, 0, 0, 0.325), rgba(0, 0, 0, 0.125)),
    url(${props => props.src});
  background-position: center;
  background-size: cover;
  ${shadows};
`
PhotoSection.defaultProps = {
  py: 4
}

const Modules = styled(Box)`
  display: grid;
  grid-gap: ${({ theme }) => theme.space[3]}px;
  ${({ theme }) => theme.mediaQueries.md} {
    grid-template-columns: repeat(3, 1fr);
  }
  > div {
    align-items: flex-start;
    padding: 0;
    text-align: left;
    width: 100% !important;
  }
  p {
    color: ${({ theme }) => theme.colors.slate};
    line-height: 1.375;
  }
`

const Notification = styled(Flex)`
  border-radius: ${({ theme }) => theme.radius};
  background-color: rgba(255, 255, 255, 0.9375);
  strong {
    font-weight: bold;
  }
`
Notification.defaultProps = {
  py: 3,
  px: 4,
  mb: 3,
  mt: 5,
  mx: 2,
  align: 'center',
  justify: 'center',
  color: 'slate',
  flexDirection: 'column'
}

const Row = Container.extend.attrs({ color: 'black', px: 3, py: [3, 4] })`
  display: grid;
  grid-gap: ${({ theme }) => theme.space[3]}px;
  text-align: left;
  ${({ theme }) => theme.mediaQueries.md} {
    grid-template-columns: ${props => props.cols || '2fr 3fr'};
    grid-gap: ${({ theme }) => theme.space[4]}px;
  }
`

const styles = {
  ultraline: { f: [6, 7], style: { lineHeight: '1.0625' } },
  headline: { f: [5, 6], style: { lineHeight: '1.125' } },
  subhline: {
    f: [3, 4],
    color: 'black',
    regular: true,
    style: { lineHeight: '1.375' }
  },
  contentContainer: {
    maxWidth: 64,
    w: 1,
    p: [3, 2]
  }
}

const title = 'Start Your Hack Club | Hack Club'
const description =
  'Learn how to start a coding club at your high school through Hack Club. Get programming club ideas, curriculum, activities, and more.'

export default () => (
  <Fragment>
    <Helmet
      title={title}
      meta={[
        { name: 'twitter:title', content: title },
        { name: 'description', content: description },
        { name: 'twitter:description', content: description },
        { property: 'og:title', content: title },
        { property: 'og:description', content: description },
        { property: 'og:url', content: 'https://hackclub.com/start' }
      ]}
    />

    <PhotoSection src="/lah_2.jpg">
      <Notification>
        <Text f={3} bold>
          Fall 2018 applications due by September 30th
        </Text>
        <Text f={2}>
          Applications are open and accepted on a rolling basis.
        </Text>
      </Notification>
      <Container maxWidth={56} p={[2, 3]} mb={[5, 6]}>
        <Heading.h1 {...styles.ultraline} color="white">
          Let’s get started.
        </Heading.h1>
        <Heading.h2 {...styles.subhline} color="white" f={[4, 5]} mt={3}>
          Whether you're interested in starting a new chapter or joining Hack
          Club with an existing CS club, we should talk.
        </Heading.h2>
      </Container>
    </PhotoSection>
    <Row my={[3, 4]}>
      <Box color="black">
        <Heading.h2 {...styles.headline} mb={3}>
          HQ provides the support to get you started
        </Heading.h2>
        <Text {...styles.subhline}>
          Get your club started with our resources used by 200+ Hack Clubs.
        </Text>
      </Box>
      <Modules>
        <Module
          icon="forum"
          heading="Community"
          body="Join our Slack and meet hundreds of other club leaders and members around the world."
          color="red.5"
        />
        <Module
          icon="pages"
          heading="Curriculum"
          body="Give your members dozens of free tutorials for making websites, apps, and games."
          color="orange.5"
        />
        <Module
          icon="voice_chat"
          heading="Mentorship"
          body="Talk to our team over a call or on Slack for guidance and assistance whenever you need help."
          color="yellow.7"
        />
        <Module
          icon="chrome_reader_mode"
          heading="Guidelines"
          body="Learn from hundreds of other clubs—we’ve got information, advice, and experience."
          color="teal.7"
        />
        <Module
          icon="local_activity"
          heading="Local events"
          body="Attend hackathons, workshops, and other events from Hack Clubs near yours."
          color="blue.5"
        />
        <Module
          icon="wallpaper"
          heading="Marketing"
          body="Get stickers, posters, and ideas for spreading the word about your amazing club meetings."
          color="indigo.5"
        />
      </Modules>
    </Row>
    <Container px={3} mt={[0, 0, -75]} mb={[3, 5]}>
      <Box color="black">
        <Heading.h3 f={[4, 5]} mt={4}>
          Already have a club?
        </Heading.h3>
        <Text f={[3, 4]} my={3}>
          Great! When established CS clubs join, they get the full benefits
          of the network. Hack Club is currently optimized for new chapters,
          but we're increasing the benefits for existing clubs every day with
          launches like{' '}
          hackathons
          {' '}and{' '}
          bank.
        </Text>
      </Box>
    </Container>
    <Container px={3} my={[3, 5]}>
      <Box color="black">
        <Heading.h2 f={[5, 6]} mt={5} mb={4}>
        What do meetings look like?
        </Heading.h2>
        <Container maxWidth={48} mx={0}>
          <Text f={[3, 4]}>
            Hack Clubs meet weekly, typically for 1.5 hours. Meetings
            resemble mini-hackathons, where members learn to code through
            building projects like{' '}
            websites
            {' '}and{' '}
            games.
          </Text>
          <Text f={[3, 4]} my={3}>
            Clubs are led by teams of 2-3 students (sorry, no parents or
            teachers). We ask that at least 1 leader be technically adept
            enough to write and customize workshops.
          </Text>
          <Heading.h3 f={[4, 5]} mt={4}>
            What happens outside of club meetings?
          </Heading.h3>
          <Text f={[3, 4]} my={3}>
            After Hack Clubs establish a dedicated base of members, they begin
            to attend{' '}
            nearby hackathons
            {' '}and eventually host their own (sometimes with{' '}
            Hack Club Bank).
          </Text>
          <Heading.h3 f={[4, 5]} mt={4}>
            Will you provide everything to make my club successful?
          </Heading.h3>
          <Text f={[3, 4]} my={3}>
            No. Every school is different and you're going to need to heavily
            customize our advice and resources. We try our best, but you know
            your school better than we do.
          </Text>
        </Container>
      </Box>
    </Container>
    <Row my={[4, 5]}>
      <Box mt={-4}>
        <Text color="accent" f={4} bold caps>
          here's the process
        </Text>
        <Heading.h2 {...styles.headline}>Apply and start your club.</Heading.h2>
      </Box>
      <Modules w={1}>
        <Module
          icon="assignment"
          heading="1. Apply"
          body="Submit your information to start—totally free."
          color="primary"
        />
        <Module
          icon="ring_volume"
          heading="2. Training call"
          body="We’ll chat and begin a plan for your club."
          color="teal.7"
        />
        <Module
          icon="event_available"
          heading="3. Lead your club!"
          body="Schedule your first meeting and get ready!"
          color="info"
        />
      </Modules>
    </Row>
    <Start
      mt={4}
      buttonProps={{ children: 'Begin Your Application', to: '/apply' }}
    />
    <PhotoSection
      src="/lah_1.jpg"
      aria-label="Students stacking cups together"
      py={[0, 250, 350]}
      my={[0, -50]}
      style={{zIndex: -1}}
    >
    </PhotoSection>

  </Fragment>
)
<commit_msg>Darken start page notification<commit_after>import React, { Fragment } from 'react'
import {
  BackgroundImage,
  Box,
  Container,
  Flex,
  Heading,
  Link as A,
  Section,
  Text
} from '@hackclub/design-system'
import styled, { css } from 'styled-components'
import Helmet from 'react-helmet'
import Link from 'gatsby-link'
import Nav from 'components/Nav'
import Module from 'components/Module'
import Framed from 'components/Framed'
import Start from 'components/Start'
import Footer from 'components/Footer'

const shadows = `
  h1,
  h2 {
    color: #fff;
    text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.75);
  }
`
const PhotoSection = styled(Section)`
  position: relative;
  background: linear-gradient(rgba(0, 0, 0, 0.325), rgba(0, 0, 0, 0.125)),
    url(${props => props.src});
  background-position: center;
  background-size: cover;
  ${shadows};
`
PhotoSection.defaultProps = {
  py: 4
}

const Modules = styled(Box)`
  display: grid;
  grid-gap: ${({ theme }) => theme.space[3]}px;
  ${({ theme }) => theme.mediaQueries.md} {
    grid-template-columns: repeat(3, 1fr);
  }
  > div {
    align-items: flex-start;
    padding: 0;
    text-align: left;
    width: 100% !important;
  }
  p {
    color: ${({ theme }) => theme.colors.slate};
    line-height: 1.375;
  }
`

const Notification = styled(Flex)`
  border-radius: ${({ theme }) => theme.radius};
  background-color: rgba(255, 255, 255, 0.9375);
  strong {
    font-weight: bold;
  }
`
Notification.defaultProps = {
  py: 3,
  px: 4,
  mb: 3,
  mt: 5,
  mx: 2,
  align: 'center',
  justify: 'center',
  color: 'gray.9',
  flexDirection: 'column'
}

const Row = Container.extend.attrs({ color: 'black', px: 3, py: [3, 4] })`
  display: grid;
  grid-gap: ${({ theme }) => theme.space[3]}px;
  text-align: left;
  ${({ theme }) => theme.mediaQueries.md} {
    grid-template-columns: ${props => props.cols || '2fr 3fr'};
    grid-gap: ${({ theme }) => theme.space[4]}px;
  }
`

const styles = {
  ultraline: { f: [6, 7], style: { lineHeight: '1.0625' } },
  headline: { f: [5, 6], style: { lineHeight: '1.125' } },
  subhline: {
    f: [3, 4],
    color: 'black',
    regular: true,
    style: { lineHeight: '1.375' }
  },
  contentContainer: {
    maxWidth: 64,
    w: 1,
    p: [3, 2]
  }
}

const title = 'Start Your Hack Club | Hack Club'
const description =
  'Learn how to start a coding club at your high school through Hack Club. Get programming club ideas, curriculum, activities, and more.'

export default () => (
  <Fragment>
    <Helmet
      title={title}
      meta={[
        { name: 'twitter:title', content: title },
        { name: 'description', content: description },
        { name: 'twitter:description', content: description },
        { property: 'og:title', content: title },
        { property: 'og:description', content: description },
        { property: 'og:url', content: 'https://hackclub.com/start' }
      ]}
    />

    <PhotoSection src="/lah_2.jpg">
      <Notification>
        <Text f={3} bold>
          Fall 2018 applications due by September 30th
        </Text>
        <Text f={2}>
          Applications are open and accepted on a rolling basis.
        </Text>
      </Notification>
      <Container maxWidth={56} p={[2, 3]} mb={[5, 6]}>
        <Heading.h1 {...styles.ultraline} color="white">
          Let’s get started.
        </Heading.h1>
        <Heading.h2 {...styles.subhline} color="white" f={[4, 5]} mt={3}>
          Whether you're interested in starting a new chapter or joining Hack
          Club with an existing CS club, we should talk.
        </Heading.h2>
      </Container>
    </PhotoSection>
    <Row my={[3, 4]}>
      <Box color="black">
        <Heading.h2 {...styles.headline} mb={3}>
          HQ provides the support to get you started
        </Heading.h2>
        <Text {...styles.subhline}>
          Get your club started with our resources used by 200+ Hack Clubs.
        </Text>
      </Box>
      <Modules>
        <Module
          icon="forum"
          heading="Community"
          body="Join our Slack and meet hundreds of other club leaders and members around the world."
          color="red.5"
        />
        <Module
          icon="pages"
          heading="Curriculum"
          body="Give your members dozens of free tutorials for making websites, apps, and games."
          color="orange.5"
        />
        <Module
          icon="voice_chat"
          heading="Mentorship"
          body="Talk to our team over a call or on Slack for guidance and assistance whenever you need help."
          color="yellow.7"
        />
        <Module
          icon="chrome_reader_mode"
          heading="Guidelines"
          body="Learn from hundreds of other clubs—we’ve got information, advice, and experience."
          color="teal.7"
        />
        <Module
          icon="local_activity"
          heading="Local events"
          body="Attend hackathons, workshops, and other events from Hack Clubs near yours."
          color="blue.5"
        />
        <Module
          icon="wallpaper"
          heading="Marketing"
          body="Get stickers, posters, and ideas for spreading the word about your amazing club meetings."
          color="indigo.5"
        />
      </Modules>
    </Row>
    <Container px={3} mt={[0, 0, -75]} mb={[3, 5]}>
      <Box color="black">
        <Heading.h3 f={[4, 5]} mt={4}>
          Already have a club?
        </Heading.h3>
        <Text f={[3, 4]} my={3}>
          Great! When established CS clubs join, they get the full benefits
          of the network. Hack Club is currently optimized for new chapters,
          but we're increasing the benefits for existing clubs every day with
          launches like{' '}
          hackathons
          {' '}and{' '}
          bank.
        </Text>
      </Box>
    </Container>
    <Container px={3} my={[3, 5]}>
      <Box color="black">
        <Heading.h2 f={[5, 6]} mt={5} mb={4}>
        What do meetings look like?
        </Heading.h2>
        <Container maxWidth={48} mx={0}>
          <Text f={[3, 4]}>
            Hack Clubs meet weekly, typically for 1.5 hours. Meetings
            resemble mini-hackathons, where members learn to code through
            building projects like{' '}
            websites
            {' '}and{' '}
            games.
          </Text>
          <Text f={[3, 4]} my={3}>
            Clubs are led by teams of 2-3 students (sorry, no parents or
            teachers). We ask that at least 1 leader be technically adept
            enough to write and customize workshops.
          </Text>
          <Heading.h3 f={[4, 5]} mt={4}>
            What happens outside of club meetings?
          </Heading.h3>
          <Text f={[3, 4]} my={3}>
            After Hack Clubs establish a dedicated base of members, they begin
            to attend{' '}
            nearby hackathons
            {' '}and eventually host their own (sometimes with{' '}
            Hack Club Bank).
          </Text>
          <Heading.h3 f={[4, 5]} mt={4}>
            Will you provide everything to make my club successful?
          </Heading.h3>
          <Text f={[3, 4]} my={3}>
            No. Every school is different and you're going to need to heavily
            customize our advice and resources. We try our best, but you know
            your school better than we do.
          </Text>
        </Container>
      </Box>
    </Container>
    <Row my={[4, 5]}>
      <Box mt={-4}>
        <Text color="accent" f={4} bold caps>
          here's the process
        </Text>
        <Heading.h2 {...styles.headline}>Apply and start your club.</Heading.h2>
      </Box>
      <Modules w={1}>
        <Module
          icon="assignment"
          heading="1. Apply"
          body="Submit your information to start—totally free."
          color="primary"
        />
        <Module
          icon="ring_volume"
          heading="2. Training call"
          body="We’ll chat and begin a plan for your club."
          color="teal.7"
        />
        <Module
          icon="event_available"
          heading="3. Lead your club!"
          body="Schedule your first meeting and get ready!"
          color="info"
        />
      </Modules>
    </Row>
    <Start
      mt={4}
      buttonProps={{ children: 'Begin Your Application', to: '/apply' }}
    />
    <PhotoSection
      src="/lah_1.jpg"
      aria-label="Students stacking cups together"
      py={[0, 250, 350]}
      my={[0, -50]}
      style={{zIndex: -1}}
    >
    </PhotoSection>

  </Fragment>
)
<|endoftext|>