-
Notifications
You must be signed in to change notification settings - Fork 2
Frontend
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.
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 sourcesApp.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 ownindex.js, which also uses react-router-dom to do routing.org/also has a sub-sub-pageadmin/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 inApp.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.
-
-
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:
The Grid is the basic component used for layout. Here are a few important things to know about Grid:
- There are two types of
Gridcomponents:<Grid container>is for the container, and<Grid item>is for each item. -
Gridis built on top ofFlexbox. If you need to do advanced layout, it is a good idea to be familiar with flexbox, as a lot ofGridoptions are the same as those inFlexbox. -
Griditems have the propertiesxs,sm,md,lg, andxl. These denote how much space aGriditem 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 byxsorsmrather thanlgorxl. 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.
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 responseThis 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 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.