Skip to content
This repository was archived by the owner on Aug 6, 2024. It is now read-only.

Frontend

David Chen edited this page Jun 26, 2023 · 9 revisions

The frontend of StuyActivities is written in JavaScript, and hosted on Netlify. It uses React, GraphQL (Apollo), and Material UI for most of the things that it does. It was created with Create React App, so a lot of the scripts are based on it (run, build, etc.). To work on StuyActivities, pull the repository and install the npm dependencies (usually with npm i). Then, just run npm start. By default, the client will connect to the live StuyActivities API. If you would like it to connect to a local API, set the REACT_APP_GRAPHQL_URI environment variable to the location of the API. For example, if you use the default options in hosting the API, you would run the program with REACT_APP_GRAPHQL_URI=http://localhost:3001/graphql npm start.

File Structure Overview

The best way to dive into the way StuyActivities works is by looking at the file structure. Here is a breakdown of some of the files:

  • package.json - this defines the dependencies, prettier parameters, and other various things. You usually won't need to touch this unless a dependency breaks.
  • src/ - the folder with all the code
    • index.js - the entryway into the program, just sources App.js
    • App.js - the real entryway. This file introduces all of the providers that are used across the site, and then sources the pages directory. For more info on providers and context, look at the React documentation.
    • pages/ - this folder contains all of the main pages.
      • index.js - this is the main file in the pages. It uses react-router-dom to do all of the routing on the front end. It also includes a few dialogs.
      • org/, admin/ - these are pages with sub-pages in them, so they are folders with their own index.js, which also uses react-router-dom to do routing. org/ also has a sub-sub-page admin/ for the admin panel of the organization page.
    • comps/ - this folder contains components that aren't entire pages but that are used on pages. It is a good idea to put components here if a page file is too long or if a certain component is used across multiple pages.
      • auth/ - components in this folder are used for authentication (!)
      • context/ - components in this folder are called in App.js
        • ApolloProvider.js - includes information on how to connect to GraphQL
        • UserProvider.js - includes information on how to sign in and sign out
      • There are more folders under here, each corresponding to a specific use.

Common Tasks

Theming and Layout

Make sure you read up on the React Material UI documentation. If you go to the menu on the left, it gives you a list of components, each with a page on how to use it. That being said, here is how most stuff goes:

Grid

The Grid is the basic component used for layout. Here are a few important things to know about Grid:

  • There are two types of Grid components: <Grid container> is for the container, and <Grid item> is for each item.
  • Grid is built on top of Flexbox. If you need to do advanced layout, it is a good idea to be familiar with flexbox, as a lot of Grid options are the same as those in Flexbox.
  • Grid items have the properties xs, sm, md, lg, and xl. These denote how much space a Grid item takes up when the container is split into 12 equal segments. Each property defines how much space it takes up on a specific range of widths. So, for example, if the container is really narrow, the item's width would be controlled by xs or sm rather than lg or xl. Using these properties helps StuyActivities stay mobile-friendly.

Custom CSS (DEPRECATED, replaced as of #601 with @mui/system, using the sx prop)

Usually, you can do everything you need to do with MUI components and their properties, but sometimes that is not the case. In these situations, it provides a handy tool called makeStyles. Here is an example of it in action:

import { makeStyles } from "@material-ui/core";
//...
const useStyles = makeStyles(theme => ({
  margin: {
    margin: theme.spacing(1),
    boxSizing: "border-box"
  },
  //...
  backButton: {
    [theme.breakpoints.up("lg")]: {
      position: "absolute",
      marginLeft: theme.spacing(10),
      marginTop: theme.spacing(3)
    },
    [theme.breakpoints.down("lg")]: {
      marginTop: theme.spacing(3)
    }
  }
}));

// In the React component...
const classes = useStyles();
// Example styled component:
return (
  <div className={classes.margin}>
    //...
  </div>
);

To learn more about this, check out the documentation.

Doing GraphQL Requests

Basic Query

NOTE: try to avoid performing redundant requests. If you see the same information going into a bunch of pages, consider doing one big request and then creating a React context to pass that information to sub-pages. An example can be seen in src/pages/org/index.js.

Doing GraphQL requests is very common, so here's a basic example on how to do a query:

import { gql, useQuery } from "@apollo/client";

const QUERY = gql`
query Organization($url: String!) {
  organization(url: $url) {
    id
    active
    name
    url
    charter {
      mission
      meetingSchedule
    }
  }
}
`;

//... in your component function
const {data, loading, refetch} = useQuery(QUERY, {variables: {url: "stuysu"}});
if (loading) {
  return "Loading...";
}
//... use data in your response

This includes GraphQL syntax. Feel free to go to https://api.stuyactivities.org/graphql to try it out. You can click on DOCS on the right to see all of the queries that can be done, and the variables that they take.

Mutations

Mutations are different from queries in that they request that something be changed in the database. Usually, they return the thing that has been changed, or true/false. Here is an example mutation, taken from src/pages/org/admin/meetings.js:

import { gql, useMutation } from "@apollo/client";
//...
const CREATE_MUTATION = gql`
	mutation CreateMeeting(
		$orgUrl: String
		$title: String!
		$description: String!
		$start: DateTime!
		$end: DateTime!
		$notifyFaculty: Boolean
		$privacy: String!
	) {
		createMeeting(
			orgUrl: $orgUrl
			title: $title
			description: $description
			start: $start
			end: $end
			notifyFaculty: $notifyFaculty
			privacy: $privacy
		) {
			id
			title
			description
			start
			end
			privacy
		}
	}
`;
//...
const [createMutation, { loading }] = useMutation(CREATE_MUTATION, {
  onCompleted() {
    //...
  },
  onError(error) {
    console.log({ error });
    //...
  },
  update(cache) {
    // org is the object that is queried in index.js and that contains all of the organization info. It is passed to the meetings.js file with a context.
    cache.reset().then(() => org.refetch());
  }
});

There are a few things that are different here, apart from the GraphQL.

The first, and perhaps most important, is the change from const { data, loading, refetch } = useQuery... to const [createMutation, { loading }] = useMutation.... The change here is that rather than performing this request instantly, the useMutation function returns another function, createMutation, that is then used later in the code (when a button is pressed). The same can be done with useQuery. The usage of this function looks like this:

//...
createMutation({
  variables: {
    orgUrl: match.params.orgUrl,
    title,
    description: description || "",
    //... more variables
  }
});
//...

The second is that there are now functions in the second argument of useMutation. The important one to look at here is update, which is run after the request completes. This update function resets the information displayed on the page by refetching the info from the server. Most update functions will look like this. It is possible to update the cache by inserting new information into it, rather than resetting (in fact, the actual create mutation in src/pages/org/admin/meetings.js does this), but the process is complicated and not really worth it, so most times it isn't done.

Clone this wiki locally